diff --git a/site/articles/_widget_automated.html b/site/articles/_widget_automated.html new file mode 100644 index 0000000..cecea78 --- /dev/null +++ b/site/articles/_widget_automated.html @@ -0,0 +1,36 @@ +<%~if Widget eq 'poll'%> +
+<%~elsif Widget eq 'feature_threads'%> + <%include _widget_threads.html%> +<%~else%> +
+ <%if Widget eq 'collapse'%> + style="<%TitleStyle%>"<%endif%> class="lnk"><%Title%> +
+ <%~if Subtitle%><%Subtitle%><%endif%> + <%if Image%><%if URL%><%endif%><%if URL%><%endif%><%endif%> + <%if Button%>
<%endif%> +
+ <%else%> + style="<%TitleStyle%>"<%endif%>><%Title%> + <%~if Subtitle%>
<%Subtitle%>
<%endif%> + <%~if Widget eq 'newsletter'%> + <%include _widget_newsletter.html%> + <%~elsif Widget eq 'calculators'%> + <%include _widget_calculators.html%> + <%~elsif Widget eq 'local_listings'%> + <%include _widget_local_listings.html%> + <%~elsif Widget eq 'category_list'%> + <%include _widget_category_list.html%> + <%~elsif Widget eq 'editors_pick'%> + <%include _widget_editors_pick.html%> + <%~elsif Widget eq 'feature_article'%> + <%include _widget_feature_article.html%> + <%~elsif Widget eq 'most_popular'%> + <%include _widget_most_popular.html%> + <%elsif Widget eq 'external'%> + <%include ../twitch/_widget_external.html%> + <%~endif%> + <%endif%> +
+<%~endif%> diff --git a/site/articles/_widget_calculators.html b/site/articles/_widget_calculators.html new file mode 100644 index 0000000..56f52ff --- /dev/null +++ b/site/articles/_widget_calculators.html @@ -0,0 +1,30 @@ +
+
+ + +
Custom Bike
+
+
+
+
+ + +
Stem
+
+
+
+
+ + +
Stack Reach
+
+
+
+
+ + +
Bar to HT
+
+
+
+
diff --git a/site/articles/_widget_category_list.html b/site/articles/_widget_category_list.html new file mode 100644 index 0000000..b6ffb4a --- /dev/null +++ b/site/articles/_widget_category_list.html @@ -0,0 +1,8 @@ +<%~set category_list_loop = Plugins::UI::fetch_links(category => $Category, max_hits => 5)%> +
+ <%~loop category_list_loop%> + + <%~endloop%> +
diff --git a/site/articles/_widget_editors_pick.html b/site/articles/_widget_editors_pick.html new file mode 100644 index 0000000..9e6e8cc --- /dev/null +++ b/site/articles/_widget_editors_pick.html @@ -0,0 +1,8 @@ +<%~set editor_list_loop = Plugins::UI::fetch_links(ids => $Articles, sb => 'Date')%> +
+ <%~loop editor_list_loop%> + + <%~endloop%> +
diff --git a/site/articles/_widget_external.html b/site/articles/_widget_external.html new file mode 100644 index 0000000..e5a52fb --- /dev/null +++ b/site/articles/_widget_external.html @@ -0,0 +1,9 @@ +<%~set external_links_loop = Plugins::UI::fetch_widget_external_links($ID)%> +
+ <%~loop external_links_loop%> + + <%~endloop%> +
diff --git a/site/articles/_widget_feature_article.html b/site/articles/_widget_feature_article.html new file mode 100644 index 0000000..aa8a57e --- /dev/null +++ b/site/articles/_widget_feature_article.html @@ -0,0 +1,22 @@ +<%~set feature_list_loop = Plugins::UI::fetch_links(ids => $Articles, sb => 'Date')%> +<%~loop feature_list_loop%> +
+ <%escape_html Name%> +
+ <%~if thumbnail_url%> + + <%~else%> + + <%~endif%> +
+
+ + <%Contact_Name%> +
+ +
+ <%Description%> + Read > +
+
+<%~endloop%> diff --git a/site/articles/_widget_local_listings.html b/site/articles/_widget_local_listings.html new file mode 100644 index 0000000..f66003e --- /dev/null +++ b/site/articles/_widget_local_listings.html @@ -0,0 +1,22 @@ +
+
+ + Coaches +
+
+
+ + Clubs +
+
+
+ + Fitters +
+
+
+ + Stores +
+
+
diff --git a/site/articles/_widget_manual.html b/site/articles/_widget_manual.html new file mode 100644 index 0000000..98ed54b --- /dev/null +++ b/site/articles/_widget_manual.html @@ -0,0 +1,6 @@ +
+ <%if Title%> style="<%TitleStyle%>"<%endif%>><%Title%><%endif%> + <%if Subtitle%>
<%Subtitle%>
<%endif%> + <%if Image%><%if URL%><%endif%><%if URL%><%endif%><%endif%> + <%if Button%>
<%endif%> +
diff --git a/site/articles/_widget_most_popular.html b/site/articles/_widget_most_popular.html new file mode 100644 index 0000000..e87bafb --- /dev/null +++ b/site/articles/_widget_most_popular.html @@ -0,0 +1,9 @@ +<%~Plugins::MostPopular::generate_popular_links%> +
+ <%~loop MostPopularLinks%> + <%rewrite_detail_url || ''%> + + <%~endloop%> +
diff --git a/site/articles/_widget_newsletter.html b/site/articles/_widget_newsletter.html new file mode 100644 index 0000000..0493c7e --- /dev/null +++ b/site/articles/_widget_newsletter.html @@ -0,0 +1,3 @@ +
+ <%include include_subscribe.html%> +
diff --git a/site/articles/_widget_threads.html b/site/articles/_widget_threads.html new file mode 100644 index 0000000..b3658e1 --- /dev/null +++ b/site/articles/_widget_threads.html @@ -0,0 +1,5 @@ +
+ style="<%TitleStyle%>"<%endif%>><%Title%> + <%~if Subtitle%>
<%Subtitle%>
<%endif%> +
+
diff --git a/site/articles/add.html b/site/articles/add.html new file mode 100644 index 0000000..37e4540 --- /dev/null +++ b/site/articles/add.html @@ -0,0 +1,60 @@ +<% set title = "Add a Link" %> + + + + <%site_title%>: <%title%> + <%-- + + + +--%> +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> +
+ <%include include_header.html%> +
+
+ + +
<%-- start content--%> +

<% title %>

+ <%~ set megabytes = 1048576 ~%> + <%~ set image_cfg = Links::Plugins::get_plugin_user_cfg('SlideShow') ~%> + <%~ set image_size = image_cfg.max_upload_size / $megabytes ~%> + <%~ set image_size = round($image_size) ~%> + <%~ set video_max_size = Plugins::ConvertVideo::get_video_max_size() ~%> + <%~ set video_size = $video_max_size / $megabytes ~%> + <%~ set video_size = round($video_size) ~%> +

+ + Please completely fill out the form, and we'll add your article as soon as possible.
+ Note: image files must be smaller than <% if image_size %><% image_size %><%else%>1<%endif%>MB and video files must be smaller than <% if video_size %><% video_size %><%else%>1<%endif%>MB. +

+ +
+ +
+ +
<%-- end content --%> +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> +<%~include include_form.js%> + diff --git a/site/articles/add_success.html b/site/articles/add_success.html new file mode 100644 index 0000000..ac42c3e --- /dev/null +++ b/site/articles/add_success.html @@ -0,0 +1,75 @@ + +<%~Plugins::UI::update_featured_links('article', $ID, $in.featured_article)%> +<%~Plugins::UI::update_featured_links('photo', $ID, $in.featured_photo)%> + + + <%site_title%>: Link Added +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> +
+ <%include include_header.html%> +
+
+ + +
<%-- start content--%> +

Link Added

+ +

+ We have received the following link: +

+ +
+ +
<%Title%>
+
+
+ +
+ <%~if Category_loop.length > 1%> +
    <%loop Category_loop%>
  • <%loop_value%>
  • <%endloop%>
+ <%~else%> + <%Category%> + <%~endif%> +
+
+
+ +
<%if Link_Type eq 'photo'%>Photo Gallery<%elseif Link_Type eq 'video'%>Video<%else%>Article<%endif%>
+
+
+ +
<%escape_html Description%>
+
+
+ +
<%escape_html Contact_Name%>
+
+
+ +
<%escape_html Contact_Email%>
+
+ +

+ <%~if config.build_auto_validate%> + Your link has been added to <%if Category_loop.length > 1%>the following categories: <%loop Category_loop%><%loop_value%><%unless last%>, <%endunless%><%endloop%><%else%><%Category%><%endif%>. + <%~else%> + Thank you! We will send you an e-mail once your link has been validated. + <%~endif%> +

+ +
<%-- end content --%> +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> +<%~include include_form.js%> + diff --git a/site/articles/articles.html b/site/articles/articles.html new file mode 100644 index 0000000..8262092 --- /dev/null +++ b/site/articles/articles.html @@ -0,0 +1,50 @@ + + + + <%site_title%> +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> +
+ <%include include_header.html%> + +
+
+ <%~set title_loop = Links::Build::build('title', 'Articles')%> + <%include include_breadcrumb.html%> +
+ +
+
+
+

Articles

+ <%~set category_loop = Plugins::UI::fetch_categories()%> + + <%~loop category_loop%> + <%~if Name like 'Bike Fit' or $Name like 'Products' or $Name like 'RD Aids'%><%nextloop%><%endif%> + <%include include_category.html%> + <%~endloop%> +
+
+
+
+
+ +
+ +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> diff --git a/site/articles/category.html b/site/articles/category.html new file mode 100644 index 0000000..e274a70 --- /dev/null +++ b/site/articles/category.html @@ -0,0 +1,48 @@ + + + + <%site_title%> +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> +
+ <%include include_header.html%> + +
+
+ <%include include_breadcrumb.html%> + <%~if not links_loop.length%> +
+ +
+
+ + +
+

<%Name%>

+ <%~loop category_loop%> + <%include include_category.html%> + <%~endloop%> +
+
+
+
+ <%~else%> + <%include subcategory.html%> + <%~endif%> + +
+ +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> diff --git a/site/articles/coaching.html b/site/articles/coaching.html new file mode 100644 index 0000000..19121c7 --- /dev/null +++ b/site/articles/coaching.html @@ -0,0 +1,137 @@ + + + + <%site_title%> +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> + +
+ <%include include_header.html%> + +
+
+ <%~set title_loop = Links::Build::build('title', 'Slowtwitch Coaching')%> + <%include include_breadcrumb.html%> + +
+ + +
+
+
+

Slowtwitch Coaching

+ +

Slowtwitch Coaching Certification Clinics are designed for the multisport or individual sport coach looking to take his or her knowledge and coaching business to the next level. By combining interactive discussions and presentations with actual hands-on coaching instruction, the Slowtwitch Coaching Certification Clinics go beyond the traditional classroom setting to provide coaches with real, practical training that directly translates to their coaching careers.

+

Slowtwitch currently offers the following coaching certifications:

+ +

Click on the links above to learn more about each clinic and see a sample schedule with descriptions of each session. +

Purpose

+

Slowtwitch Coaching Certification Clinics are designed to educate coaches on how to better train their + athletes on specific aspects of swimming, biking, and running. By focusing on one specific topic, these programs are able to + delve deeper, thereby giving coaches practical tools that translate into + real-life training implementation strategies for working with clients, athletes and teams. More importantly, + coaches are given an opportunity to practice these skills through direct, hands-on coaching experience with + real athletes. +

Over the course of 2 days, coaches learn how to work with athletes of all levels from the basic beginner + to the most advanced. Beginning from the initial athlete introduction, the Slowtwitch Coaching Certification + Clinics take coaches along the journey of how to accurately assess an athlete, create an effective training + plan, measure progress and skill development, and spot the need for change along the way within an individual + sport perspective. +

Get Slowtwitch Certified

+

Prior to certification, a coach must complete and/or submit the following:

+ +

Slowtwitch Coaching Certification Clinic

+

The clinic requirement is met by attending a Slowtwitch coaching certification clinic. Any travel arrangements that cannot be cancelled without a full refund should not be made before registering for a clinic as each clinic schedule varies and attendance is capped at 20-40, depending on the topic. The maximum number for each clinic will be posted on the registration site.

+

The clinic is a 2-day practicum and classroom presentation combination course with opportunities to learn and network. Participants must attend the entire clinic in order to be eligible for certification. The minimum age for participation is 18. The fee of $399 includes 2 full days of the clinic, registration fees, clinic manual, and exam. It does not include lodging, meals, travel expenses or the background check fee.

+

Directions for completing the online written exam are given at the end of the clinic. The provided course manual, classroom instruction, and hands-on portions of the clinic are the basis for the exam. Attendees are given 3 months to complete the exam. After the 3 month deadline, late fees apply for submission.

+
+
+
Registration Opens
+
Clinic Location/Dates
+
Certification Topic
+
Registration Link
+
+
+
May 31, 2016
+
August 4-5, 2016
Denver, CO
+
Triathlon Cycling
+ +
+
+
June 15, 2016
+
September 22-23, 2016
Las Vegas, NV
+
Triathlon Cycling Power
+ +
+
+

More dates coming soon!

+
+
+

Background Check

+

Coaching certification candidates must pass a criminal history check through the NCSI background check + application prior to attending a clinic in order to become certified. The fee is $21.25 for most U.S. + citizens but does vary by + state. International background checks cost more and vary by country. Fees for the background check are not + included in the clinic registration fee. Each candidate must complete and pass the required background + check or entry into the clinic and certification are denied.

+

Background checks are based on this criteria. A failed background check results in the + candidate being ineligible for participation in a coaching clinic and certification. There is no appeal + process for failed background checks and no exceptions. Any candidate concerned about his or her + background should complete the background check prior to registration.

+ +

CPR

+

A current CPR or AED certification is required at the time of the exam submission in order to be + certified. It may be youth or adult and can be taken online.

+ +

Certification Period

+

Slowtwitch Coaching Certifications do not expire. Coaches are encouraged to continue their education on their own and return to a course in the future as they are continually evolving.

+ +

Bring a Clinic to You

+

If you would like a clinic brought to your facility, please contact coaching@slowtwitch.com with the desired dates, clinic topic, location and capabilities of the facility (pool, track, classroom, etc.). Classroom space is required for all clinics. Ancillary needs like pool space, a track or fields for bike drills vary based on the desired clinic. Please note that you must be able to guarantee a minimum of 10 registrations to be considered.

+ +

Questions

+

Please email coaching@slowtwitch.com + with any questions you have about the Slowtwitch Coaching Education Program.

+
+ +
+
+
+
+ +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/articles/detailed.html b/site/articles/detailed.html new file mode 100644 index 0000000..c7dad89 --- /dev/null +++ b/site/articles/detailed.html @@ -0,0 +1,186 @@ +<%~set detailed_url = get_detailed_url($ID)%> +<%~Links::Utils::load_reviews($ID, $detailed_max_reviews)~%> +<%~Plugins::SlideShow::generate_paths($ID)%> +<%~convert_nl_br%> +<%old_detailed_url($detailed_url)%> + + + + <%Title%> - <%site_title_short%> + <%--Facebook Meta Tags--%> + + + + + + + + + <%--Twitter Meta Tags--%> + + + + + + <%~if Image1_thumbnail_path%> + + + + <%~elsif Thumbnail_URL and Thumbnail_URL ne 'http://' %> + + + + <%else%> + <%~Plugins::ConvertVideo::get_file_path($ID, "thumbnail_file_field") %> + <%~if thumbnail_file_field_path%> + + + + <%~endif%> + <%~endif%> + <%~include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> + +
+ <%include include_header.html%> + +
+
+ <%include include_breadcrumb.html%> + +
+ + +
+
+
+

<%Title%>

+ + +
+
+
+ + <%~if Link_Type eq 'article'%> + <%~include include_detailed_article.html%> + <%~elsif Link_Type eq 'photo' %> + <%~if slideshow%> + <%~include include_detailed_slideshow.html%> + <%~elsif is_old_article($Add_Date_orig)%> + <%~include include_detailed_old_photo.html%> + <%else%> + <%~include include_detailed_photo.html%> + <%~endif%> + <%~else%> + <%~include include_detailed_video.html%> + <%~endif%> + +
+ <%~if user.Status eq 'Administrator'%> + EDIT THIS ARTICLE + EDIT FEATURED LINKS + <%~endif%> + <%if user.Status eq 'Editor' AND user.Status neq 'Administrator'%>EDIT THIS ARTICLE<%endif%> + <%if user.Status eq 'Administrator'%>Log Out<%endif%> + <%if config.bookmark_enabled%>Bookmark It<%endif%> + <%if isLinkOwner%>Edit this link<%endif%> + <%if paymentsEnabled%><%if isUnpaid or isFree%>New Payment<%else%>Renewal Payment<%endif%><%endif%> +
+ + <%include include_share.html%> + <%include include_fb_comments.html%> +
+
+
+ +
+
+
+
+ +
+ + + +
+ + + +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/articles/error.html b/site/articles/error.html new file mode 100644 index 0000000..1f09419 --- /dev/null +++ b/site/articles/error.html @@ -0,0 +1,19 @@ +<%set error_message = $error~%> +<%set error = ''~%> + + + <%site_title%>: Error +<%include include_common_head.html%> + + +

Error

+ +

+ Oops, we had the following problem: +

+

+ <%error_message%> +

+ + + diff --git a/site/articles/featured_links.html b/site/articles/featured_links.html new file mode 100644 index 0000000..f0e0567 --- /dev/null +++ b/site/articles/featured_links.html @@ -0,0 +1,61 @@ + + + + <%site_title%>: Featured Links +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> +
+ <%include include_header.html%> +
+
+ + +
<%-- start content--%> +

Featured Links

+ +
+ + <%if error%>

<%error%> + <%elsif message%>

<%message%><%endif%> +

+
+ +
+ + + + +
+
+
+ +
+ + +
+
+
+
+
+ +
+
+
+
+ +
<%-- end content --%> +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> +<%~include include_form.js%> + diff --git a/site/articles/home.html b/site/articles/home.html new file mode 100644 index 0000000..ab9b392 --- /dev/null +++ b/site/articles/home.html @@ -0,0 +1,258 @@ + + + + <%site_title%> +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> + +
+ <%include include_header.html%> + +
+
+ <%~if $config.featured_articles%><%set featured_articles_loop = Plugins::UI::fetch_links(ids => $config.featured_articles)%><%endif%> + <%~if featured_articles_loop.length%> + +
+ <%~endif%> + + <%~set widgets_loop = Plugins::UI::fetch_widgets('home')%> +
+ +
+
+ +
+

Latest Headlines

+ <%~set latest_news = Plugins::UI::fetch_links(type => 'news', max_hits => 6)%> + <%~set list_view = 'latest'%> + <%~if latest_news.length%> + <%~loop latest_news%> + <%include link.html%> + <%~endloop%> + <%~endif%> +
+ + + <%~set latest_tech = Plugins::UI::fetch_links(type => 'editorial', max_hits => 3)%> + <%~set list_view = 'latest'%> + <%~if latest_tech.length%> +
+ <%~set adsize = '300x250_1'%> + <%include include_ad.html%> + +

Latest Tech

+ <%~loop latest_tech%> + <%include link.html%> + <%~endloop%> +
+ <%~endif%> + +
+
+ +
+
+
+ + <%-- Featured photo & video section --%> + <%~if $config.featured_photos%><%set featured_photo_video_loop = Plugins::UI::fetch_links(ids => $config.featured_photos)%><%endif%> + <%~if featured_photo_video_loop.length%> + +
+ <%~endif%> + + + <%-- Begin Inside Track --%> + <%~set interviews = Plugins::UI::fetch_links(category => 7, max_hits => 6)%> + <%~if interviews.length%> +
+ +
+
+
+
+

Inside Track

+ Interviews +
+
+
+
+ <%~loop interviews%> +
+ + <%~if thumbnail_url%> +
+ <%~else%> + + <%~endif%> +
+
+

<%escape_html Title%>

+ +
+
+ <%if row_num == 3%>
<%endif%> + <%~endloop%> + + +
+
+
+
+
+
+
+ <%~endif%> + <%-- End Inside Track --%> + + <%-- Begin Featured Articles --%> + <%~set features = Plugins::UI::fetch_links(category => 9, max_hits => 12)%> + <%~if features.length%> +
+ +
+
+
+ +

Feature Articles

+ <%~set list_view = 'column'%> + <%~loop features%> + <%include link.html%> + <%~endloop%> + + +
+ +
+
+
+
+ <%~endif%> + <%-- End Featured Articles --%> + + + +
+ +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/articles/include_ad.html b/site/articles/include_ad.html new file mode 100644 index 0000000..5e67f4e --- /dev/null +++ b/site/articles/include_ad.html @@ -0,0 +1,28 @@ +<%~if adsize eq '300x600'%> +
+ +
+<%~elsif adsize eq '970x250'%> +
+ +
+<%~elsif adsize eq '728x90' and $ad_728x90%> +
+ +
+<%~elsif adsize eq '300x250_1'%> + +<%~elsif adsize eq '300x250'%> + +<%~endif%> diff --git a/site/articles/include_breadcrumb.html b/site/articles/include_breadcrumb.html new file mode 100644 index 0000000..3f79d6a --- /dev/null +++ b/site/articles/include_breadcrumb.html @@ -0,0 +1,13 @@ + diff --git a/site/articles/include_category.html b/site/articles/include_category.html new file mode 100644 index 0000000..d802a6e --- /dev/null +++ b/site/articles/include_category.html @@ -0,0 +1,25 @@ +<%~set latest_links = Plugins::UI::fetch_links(category => $ID, max_hits => 3)%> +<%~if latest_links.length%> +
+
+ <%~if latest_links.length and $latest_links.0.thumbnail_url%> + + <%~else%> + + <%~endif%> +
+
+ <%escape_html Name%> + <%~if latest_links.length%> + + <%~endif%> +
+ More +
+
+
+<%~endif%> diff --git a/site/articles/include_common_head.html b/site/articles/include_common_head.html new file mode 100644 index 0000000..55c9174 --- /dev/null +++ b/site/articles/include_common_head.html @@ -0,0 +1,6 @@ +<%~set minify = $config.minify || 0%> +<%include include_global_head.html%> +<%google_analytics%> +<%ga_link_tracker%> +<%comscore%> +<%quantcast%> diff --git a/site/articles/include_content_header.html b/site/articles/include_content_header.html new file mode 100644 index 0000000..e69de29 diff --git a/site/articles/include_detailed_article.html b/site/articles/include_detailed_article.html new file mode 100644 index 0000000..dc597ad --- /dev/null +++ b/site/articles/include_detailed_article.html @@ -0,0 +1,12 @@ + + + diff --git a/site/articles/include_detailed_old_photo.html b/site/articles/include_detailed_old_photo.html new file mode 100644 index 0000000..b95635f --- /dev/null +++ b/site/articles/include_detailed_old_photo.html @@ -0,0 +1,22 @@ +
+

<%Paragraph1%>

+ <%~set count = 0%> + <%~loop 1 .. 20%> + <%~set count += 1%> + <%~set image_path = Plugins::UI::retrieve_param("Image_largest_path", $loop_value)%> + <%~set description = Plugins::UI::retrieve_param('Image_description', $loop_value)%> + <%~if description%> +

<%description%>

+ <%~endif%> + <%~if image_path%> +

+ +

+ <%~if count == 1%> +
+ Slideshow +
+ <%~endif%> + <%~endif%> + <%~endloop%> +
diff --git a/site/articles/include_detailed_photo.html b/site/articles/include_detailed_photo.html new file mode 100644 index 0000000..bf7f9be --- /dev/null +++ b/site/articles/include_detailed_photo.html @@ -0,0 +1,22 @@ +
+

<%Paragraph1%>

+ <%~set count = 0%> + <%~loop 1 .. 20%> + <%~set count += 1%> + <%~set image_path = Plugins::UI::retrieve_param("Image_largest_path", $loop_value)%> + <%~set description = Plugins::UI::retrieve_param('Image_description', $loop_value)%> + <%~if image_path%> +

+ +

+ <%~if count == 1%> +
+ Slideshow +
+ <%~endif%> + <%~endif%> + <%~if description%> +

<%description%>

+ <%~endif%> + <%~endloop%> +
diff --git a/site/articles/include_detailed_slideshow.html b/site/articles/include_detailed_slideshow.html new file mode 100644 index 0000000..13e7197 --- /dev/null +++ b/site/articles/include_detailed_slideshow.html @@ -0,0 +1,53 @@ +<%~init array photos_loop%> +<%~set index = 0%> +<%~loop 1 .. 20%> + <%~set image_path = Plugins::UI::retrieve_param("Image_largest_path", $loop_value)%> + <%~set image_caption = Plugins::UI::retrieve_param('Image_description', $loop_value)%> + <%~set thumbnail_path = Plugins::UI::retrieve_param("Image_thumbnail_path", $loop_value)%> + <%~if image_path%> + <%~init hash image%><%set image.path = $image_path%><%set image.caption = $image_caption%><%set image.thumb = $thumbnail_path%> + <%~set photos_loop.$index = $image%> + <%~set index += 1%> + <%~endif%> +<%~endloop%> +
+
+ <%~loop photos_loop%> +
+
+ +
<%row_num%> of <%photos_loop.length%> photo<%if photos_loop.length > 1%>s<%endif%>
+ <%~if $photos_loop.length%> +
+
<
+
>
+
+ <%~endif%> +
+
+ <%caption || ''%> +
+
+ <%~endloop%> +
+
+ +<%~if photos_loop.length%> +
+
PREV
+
NEXT
+
1 of <%photos_loop.length%> photo<%if photos_loop.length > 1%>s<%endif%>
+
+ +
+
+ <%~loop photos_loop%> +
+ <%~endloop%> +
+ +
>
+
<
+
+<%~endif%> + diff --git a/site/articles/include_detailed_video.html b/site/articles/include_detailed_video.html new file mode 100644 index 0000000..b777884 --- /dev/null +++ b/site/articles/include_detailed_video.html @@ -0,0 +1,29 @@ +<%~if URL and URL ne 'http://' %><%set video = $URL%><%endif%> +<%~if Image_URL and Image_URL ne 'http://' %><%set image = $Image_URL%><%endif%> +<%~if video%> + <%~if not image%> + <%~Plugins::ConvertVideo::get_file_path($ID, "image_file_field") %> + <%~set image = $image_file_field_path %> + <%~endif %> + <%~if not video%> + <%~Plugins::ConvertVideo::get_file_path($ID, "flash_file_field") %> + <%~set video = $flash_file_field_path %> + <%~endif%> + <%~Plugins::ConvertVideo::get_flash_dimension() %> + +
+ +<%~endif%> +
+ <%Paragraph1 || ''%> +
diff --git a/site/articles/include_fb_comments.html b/site/articles/include_fb_comments.html new file mode 100644 index 0000000..65a71bc --- /dev/null +++ b/site/articles/include_fb_comments.html @@ -0,0 +1,9 @@ +
+
+ diff --git a/site/articles/include_featured_articles.html b/site/articles/include_featured_articles.html new file mode 100644 index 0000000..97033aa --- /dev/null +++ b/site/articles/include_featured_articles.html @@ -0,0 +1,18 @@ + diff --git a/site/articles/include_featured_photo_video.html b/site/articles/include_featured_photo_video.html new file mode 100644 index 0000000..163cf53 --- /dev/null +++ b/site/articles/include_featured_photo_video.html @@ -0,0 +1,47 @@ +<%~if $config.featured_photos%><%set featured_photo_video_loop = Plugins::UI::fetch_links(ids => $config.featured_photos)%><%endif%> +<%~if featured_photo_video_loop.length%> + +
+<%~endif%> diff --git a/site/articles/include_form.html b/site/articles/include_form.html new file mode 100644 index 0000000..3f3a9c9 --- /dev/null +++ b/site/articles/include_form.html @@ -0,0 +1,174 @@ +<%~if user.Status eq 'Administrator'%> +
+
Featured Link:
+
+ <%~if in.modify%> + <%~set featured_article = $in.featured_article%> + <%~set featured_photo = $in.featured_photo%> + <%~elsif $ID%> + <%~set featured_article = Plugins::UI::is_featured('article', $ID)%> + <%~set featured_photo = Plugins::UI::is_featured('photo', $ID)%> + <%~endif%> + checked<%endif%>> + checked<%endif%>> +
+
+<%~endif%> +
+
Tag:
+
+ checked<%endif%>> + checked<%endif%>> + checked<%endif%>> +
+
+
+
Title: *
+
+ +
+
+
+
Category:<%unless category_loop_selected%> *<%endunless%>
+
+ <%~if config.db_gen_category_list == 2%> + + <%~set selected_cats = Links::Tools::category_list_selected%> + <%~loop selected_cats%> + + <%~endloop%> + +
+ + <%~elsif category_loop_selected%> + <%~if category_loop.length > 1%> + + <%~else%> + <%loop category_loop%><%Full_Name%><%endloop%> + <%~endif%> + <%~else%> + + <%~endif%> +
+
+
+
Type: *
+
+ +
+
+
+
Link Type: *
+
+ <% if modify and Link_Type%> + + <%if Link_Type eq 'photo'%>Photo Gallery<%elseif Link_Type eq 'video'%>Video<%else%>Article<%endif%> + <% else %> + + <% endif%> +
+
+
+
Description:
+
+ +
+
+
+
Contact Name:
+
+ +
+
+
+
Contact E-mail:
+
+ +
+
+
+
Add Date:
+
+ +
+
+
+
Mod Date:
+
+ +
+
+
style="display:none"<% endif %>> +
Main Body:
+
+ +
+
+
style="display:none"<%endif%>> + <% include include_form_video.html %> +
+
style="display:none"<%endif%>> + <% include include_form_article_photo.html %> +
+ +
+
Related Article IDs:
One per line
+
+ +
+
+<%-- +
+
Related Article #1:
+
+ +
+
+
+
Related Article #2:
+
+ +
+
+
+
Related Article #3:
+
+ +
+
+
+
Related Article #4:
+
+ +
+
+
+
Related Article #5:
+
+ +
+
+--%> diff --git a/site/articles/include_form_article_photo.html b/site/articles/include_form_article_photo.html new file mode 100644 index 0000000..1c1d399 --- /dev/null +++ b/site/articles/include_form_article_photo.html @@ -0,0 +1,281 @@ +<% set show_photo = 0 %> +<% if Link_Type eq 'photo' %> +<% set show_photo = 1 %> +<% endif %> +
style="display:none"<% endif %>> +
Does this article have a video embedded?
+
+ +
+
+ +<%-- +
style="display:none"<% endif %>> +
Use Large Pictures Template?
+
+ +
+
+--%> +<%loop 1 .. 20%> +<%~set image_context = get_image_context($loop_value)%> +
style="display:none"<% endif %>> +
Description for Image #<%loop_value%>:
+
+ +
+
+
style="display:none"<% endif %>> +
Paragraph #<%loop_value%>:
+
+ +
+
+
+
Image #<%loop_value%>
+
+ +
+
+<%~endloop%> + +<%-- +
+
+ Image #2: +
+
style="display:none"<% endif %>> +
Description for Image #2:
+ +
+
style="display:none"<% endif %>> +
Paragraph #2:
+ +
+
+
+
style="display:none"<% endif %>> +
Description for Image #3:
+ +
+
style="display:none"<% endif %>> +
Paragraph #3:
+ +
+
+ Image #3: +
+
+
+
+ Image #4: +
+
style="display:none"<% endif %>> +
Description for Image #4:
+ +
+
style="display:none"<% endif %>> +
Paragraph #4:
+ +
+
+
+
style="display:none"<% endif %>> +
Description for Image #5:
+ +
+
style="display:none"<% endif %>> +
Paragraph #5:
+ +
+
+ Image #5: +
+
+
+
+ Image #6: +
+
style="display:none"<% endif %>> +
Description for Image #6:
+ +
+
style="display:none"<% endif %>> +
Paragraph #6:
+ +
+
+
+
style="display:none"<% endif %>> +
Description for Image #7:
+ +
+
style="display:none"<% endif %>> +
Paragraph #7:
+ +
+
+ Image #7: +
+
+
+
+ Image #8: +
+
style="display:none"<% endif %>> +
Description for Image #8:
+ +
+
style="display:none"<% endif %>> +
Paragraph #8:
+ +
+
+
+
style="display:none"<% endif %>> +
Description for Image #9:
+ +
+
style="display:none"<% endif %>> +
Paragraph #9:
+ +
+
+ Image #9: +
+
+
+
+ Image #10: +
+
style="display:none"<% endif %>> +
Description for Image #10:
+ +
+
style="display:none"<% endif %>> +
Paragraph #10:
+ +
+
+
+
style="display:none"<% endif %>> +
Description for Image #11:
+ +
+
style="display:none"<% endif %>> +
Paragraph #11:
+ +
+
+ Image #11: +
+
+
+
+ Image #12: +
+
style="display:none"<% endif %>> +
Description for Image #12:
+ +
+
style="display:none"<% endif %>> +
Paragraph #12:
+ +
+
+
+
style="display:none"<% endif %>> +
Description for Image #13:
+ +
+
style="display:none"<% endif %>> +
Paragraph #13:
+ +
+
+ Image #13: +
+
+
+
+ Image #14: +
+
style="display:none"<% endif %>> +
Description for Image #14:
+ +
+
style="display:none"<% endif %>> +
Paragraph #14:
+ +
+
+
+
style="display:none"<% endif %>> +
Description for Image #15:
+ +
+
style="display:none"<% endif %>> +
Paragraph #15:
+ +
+
+ Image #15: +
+
+
+
+ Image #16: +
+
style="display:none"<% endif %>> +
Description for Image #16:
+ +
+
style="display:none"<% endif %>> +
Paragraph #16:
+ +
+
+
style="display:none"<% endif %>> +
+
+
Description for Image #17:
+ +
+
+ Image #17: +
+
+
+
+ Image #18: +
+
+
Description for Image #18:
+ +
+
+
+
+
Description for Image #19:
+ +
+
+ Image #19: +
+
+
+
+ Image #20: +
+
+
Description for Image #20:
+ +
+
+--%> diff --git a/site/articles/include_form_video.html b/site/articles/include_form_video.html new file mode 100644 index 0000000..64554c7 --- /dev/null +++ b/site/articles/include_form_video.html @@ -0,0 +1,61 @@ +<%~if File_Path%> +<%set selectfile = 1 %> +<% else %> +<%set selectfile = 0 %> +<%endif~%> +
+
Selection:
+
+ checked<%endif%> value="website"> + checked<%endif%> value="file"> +
+
+
style="display:none"<%endif%>> +
URL:
+
+ +
+
+
style="display:none"<%endif%>> +
Image:
+
+ <%if Image_Path%> <%Image_Path%><%endif%> +
+
+
style="display:none"<%endif%>> +
File:
+
+ <%if File_Path%> <%File_Path%><%endif%> +
+
+
style="display:none"<%endif%>> +
Generate Thumbnail and Image:
+
+ + +
Select this checkbox if you want to generate the associated video files (for example, image file, flash file) while uploading the video file. This process may take several minutes.
+
+
+ diff --git a/site/articles/include_paging.html b/site/articles/include_paging.html new file mode 100644 index 0000000..fa5d60e --- /dev/null +++ b/site/articles/include_paging.html @@ -0,0 +1,14 @@ +
+ <%~if not page_button%><%set page_button = 'page_button1'%><%endif%> + <%Links::Utils::paging( + button_id => $page_button, + style_next => 'Next ', + style_prev => ' Prev', + style_first => '', + style_last => '', + style_nonext => 'Next ', + style_noprev => ' Prev', + style_nofirst => '', + style_nolast => '', + )%> +
diff --git a/site/articles/include_share.html b/site/articles/include_share.html new file mode 100644 index 0000000..e3debaa --- /dev/null +++ b/site/articles/include_share.html @@ -0,0 +1,19 @@ +
+ + + +
+ + + + + + + +
diff --git a/site/articles/include_sidebar.html b/site/articles/include_sidebar.html new file mode 100644 index 0000000..e69de29 diff --git a/site/articles/include_single_widget.html b/site/articles/include_single_widget.html new file mode 100644 index 0000000..0a08320 --- /dev/null +++ b/site/articles/include_single_widget.html @@ -0,0 +1,5 @@ +<%~if Type%> +<%include _widget_automated.html%> +<%~else%> +<%include _widget_manual.html%> +<%~endif%> diff --git a/site/articles/include_stackreach_content.html b/site/articles/include_stackreach_content.html new file mode 100644 index 0000000..586af0d --- /dev/null +++ b/site/articles/include_stackreach_content.html @@ -0,0 +1,5233 @@ +

2011 INDUSTRY DATABASE
+ (to be updated until all relevant tri bikes are included)

+

Manufacturers are listed below alphabetically. Click on an entry to jump to that manufacturer. Each table below contains the company's stack & reach measures, per size, along with other relevant data. Click here for a primer on stack & reach sizing.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ARGON18BLUEBH
BMCCALFEECANNONDALE
CEEPOCERVELOELITE
FELTFOCUSFUJI
GIANTJAMISKESTREL
KUOTALEADERLOOK
ORBEAPARLEEPLANET X
QUINTANA ROORIDLEYSCOTT
SPECIALIZEDTITANFLEXTREK
VALDORAWILIER
+
+To help explain and expand on these tables, please consider the reading below: +
+

NOTES ON THE STACK & REACH TABLE
+ FIXING BAD CENTER-OF-MASS THROUGH CUSTOM GEOMETRY
+ HEADSETS AND SPACERS
+ WEB RESOURCES

+
+

NOTES ON THE STACK & REACH TABLE
+

+ - All stack and reach measures are taken from the center of the bottom bracket to the top of the head tube, midway between its fore and aft termini. There will probably be a few millimeters of headset cap to add, and this will slightly change per each manufacturer. If the headset is external you'll need to account for this, and we explain how to do this below.
+
+ - The seat angle is listed because of the common occurence of proprietary aero seat tube/post complexes that cannot be swapped for aftermarket posts. Keep in mind that at best you're looking at 2 degrees of adjustability fore and aft (4 degrees total) through moving the saddle fore and aft (excepting 2-position seat posts and the Giant's post, the clamp of which sits on a set of "ways." So, if your customer needs 80 degrees and the bike is built at 76.5, or needs 76 degrees and the bike is built at 79, you'll probably need to look for another bike if the post is unswappable.
+
+ - The front/center measure is included not because of any issue of fit, but of handling. If the rider chooses a somewhat steep configuration, you'll want some extra front/center for the purpose of achieving a better weight distribution.

+
FIXING BAD CENTER-OF-MASS USING CUSTOM GEOMETRY
+
+
+

If a rider's center of mass is either too far forward on the bike, or too high above the bike, the best way to fix this on a tri bike is usually to increase the bike's front center. This is explained a bit in our article on short-torsoed riders. The chart below will give you some idea of the change in the bike's front/center you get by shallowing the head angle a half-degree combined with an increase in the fork's offset so as to achieve the same trail. In this case, I'm using certain given wheel radii for 650c and 700c that are subject to change, based on the profile of the tire you use. I'm also arbitrarity choosing an amount of trail typical (but not universal) in tri bike design. As you see, you'll add about 1.5cm of front/center for every degree you slacken the head angle.
+
+

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
650c (315mm tire radius)
+
+
Head angle
+
+
Offset
+
+
trail
+
+
Change in F/C
+
+
74.0
30
59.1
Baseline
73.5
33
58.9
add .75cm versus baseline
73.0
36
58.7
add 1.5cm versus baseline
72.5
39
58.4
add 2.3cm versus baseline
72.0
41
59.2
add 3.0cm versus baseline
    
700c (336mm tire radius)
+
+
74.0
36
58.9
Baseline
73.5
39
58.9
add .75cm versus baseline
73.0
42
58.8
add 1.5cm versus baseline
72.5
45
58.8
add 2.2cm versus baseline
72.0
48
58.7
add 2.9cm versus baseline
71.5
51
58.6
add 3.7cm versus baseline
71.0
54
58.6
add 4.5cm versus baseline
+
+
+

My purpose in highlighting the above relates to the long-legged, short-torsoed athlete described here. In this case, it's often observed that the rider's center of mass sits too high above the ground relative to the wheelbase of that rider's bike, should he be fitted on a bike that corresponds to his cockpit distance. This is more problematic in triathlon than it is in road race, because of the problematic weight displacement steep-angled riders face, and because the "pursuit" position tends to place the rider higher than the road rider's hooks position. Should the rider want to keep his position intact, and should the fitter/designer want to keep his steering geometry intact, the best fix is to add front/center. The table above roughly quantifies the delta when executing that change.
+

+
+
+

ACCOUNTING FOR EXTERNAL HEADSETS AND SPACERS

+
+
+

A frame's stack & reach are calculated to the head tube top. What of bikes that use external headsets? (also see integrated headsets.) How do you account for this? You can't simply say, "Add 2cm of stack," because the steering axis isn't 90 degrees, it's positioned at 72 degrees or thereabouts. Plus, if you're adding spacers into the mix, you must account for these. Let us say your proposed bike uses an external headset plus you'll want 1.5cm of spacers underneath the stem. Most headsets average about 2cm in stack above the head tube top. So you've got 3.5cm to account for.
+
+ The sine of 72 degrees is very close to Sine. The cosine of 72 degrees is very close to Cosine. These numbers are good enough to use for every head angle, because you're looking for precision within 1mm or 2mm, nothing more. So, in the case of 3.5cm of "angled stack," you multiply this times the sine and cosine of 72 degrees, and you get 3.3cm and 1.1cm respectively.
+
+ Let's try an example. Let's say that your fit simulator, with stem right down on the simulator's plate (analogous to your stem sitting right on your head tube top), shows you need a frame with a stack and reach of 58.7cm and 43.5cm respectively. Let's say you're considering a Calfee for your customer. You have to account for the Calfee's external headset. This headset, plus perhaps 1.5cm of spacers you might want under the stem, is that 3.5cm of "angled stack" described above. This 3.5cm of stack "angled back" at 73 degrees drives the stack of the frame you'll need down 3.3cm (3.5cm x sine73 = 3.3cm). So the frame's stack you'll need is really 54.4cm, not 58.7cm. This 3.5cm of stuff above the head tube top "shortens" the rider's cockpit by 3.5cm x cosine(73) = 1.1cm. So the actual reach you'll need on your hoped-for Calfee is 44.6cm instead of 43.5cm.
+
+ So now you're looking for a frame with a stack and reach of 54.4cm and 44.6cm respectively, not the 58.7cm and 43.5cm you thought you'd need prior to accounting for the "stuff" above the head tube. Calfee offers a production geometry with a 54.7cm and 44.5cm stack and reach, its 58cm tri bike in 700c. Assuming you outfit this frame with the same length and pitch of stem, and same aerobars, as you're using on the fit simulator, take out a 5mm spacer and sub-in a 2mm spacer, and you'll replicate the fit achieved on the simulator to within 1mm.
+
+ INTEGRATED HEADSETS: Keep in mind that the bike's published stack must often be added to, because there are head parts that rise above the head tube top even with integrated headsets. This may be 2mm, and it may be 10mm and more.

+
+
+

WEB RESOURCES

+
+
+

There are plenty of good (and even free) resources on the web. The best (that I've found) among the parametric programs that would aid a framebuilder or designer is an Excel-based framebuilding tool by Martin Manning, called Body Geometry 101. Note you'll have to register to join the Framebuilders Forum in order to find and download it.

+
+
+
+
+

STACK & REACH TABLES

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
ARGON18
XS
48.5
39.8
700C
int
80
6.5/8.5*
Low
76/78
577
 
S
49.5
41.1
700C
int
90
6.5/8.5*
Low
76/78
594
E-114
M
51.5
41.9
700C
int
110
6.5/8.5*
Low
76/78
607
E-112
L
53.6
42.8
700C
int
135
6.5/8.5*
Low
76/78
622
           
 
XS
49.9
39.1
700C
Int
   
76/78
577
MERCURY
S
49.9
40.8
700C
Int
   
76/78
594
 
M
51.9
41.5
700C
Int
   
76/78
607
 
L
53.6
42.5
700C
Int
   
76/78
622
*The E-114 features the ONEness Concept integrated stem and aerobars.
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
BLUE
XS
49.1
36.4
700C
int
95
N/A
N/A
76-79
559
 
S
50.5
37.2
700C
int
103
N/A
N/A
76-79
554
T14
M
51.3
39.5
700C
int
111
N/A
N/A
76-79
580
 
ML
52.6
40.7
700C
int
125
N/A
N/A
76-79
595
 
L
55.0
42.2
700C
int
150
N/A
N/A
76-78
616
 
XL
57.2
42.6
700C
int
173
N/A
N/A
76-78
629
           
SM
52.0
37.8
700C
int
100
N/A
N/A
76-79
56.5
 
MD
53.9
39.8
700C
int
120
N/A
N/A
76-79
59.1
Triad
ML
55.8
41.4
700C
int
140
N/A
N/A
76-79
61.2
 
LG
57.7
42.9
700C
int
160
N/A
N/A
76-79
63.3
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
BH
52
520
390
700C
int
110
  
74-78
N/A
 
54
536
406
700C
int
130
  
74-78
N/A
GC AERO
56
560
420
700C
int
150
  
74-78
N/A
 
58
579
432
700C
int
170
  
74-78
N/A
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
BMC
S
480
393
700C
int
*
 
77
590
 
M-S
515
377
700C
int
*
 
77
583
TM01
M-L
515
418
700C
int
*
 
77
624
 
L
556
442
700C
int
*
 
77
661
*The TM01 uses a proprietary stem setup
All sizes have a trail of 60.2mm
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
CALFEE
46
45.7
37.8
650C
ext
91
**
**
77.0
55.3
 
48
46.3
40.3
650C
ext
91
**
**
77.0
56.8
 
50
47.2
41.9
650C
ext
100
**
**
77.0
58.7
 
52
48.4
42.5
650C
ext
113
**
**
77.0
59.7
54
49.8
43.6
650C
ext
128
**
**
76.5
61.2
ALL
56
51.9
45.0
650C
ext
150
**
**
76.5
63.2
56
54.2
44.0
700C
ext
128
**
**
76.5
64.0
58
53.3
45.4
650C
ext
160
**
**
76.5
64.0
58
54.7
44.9
700C
ext
133
**
**
76.0
65.0
60
54.8
46.5
650C
ext
180
**
**
76.0
65.6
60
57.0
46.1
700C
ext
165
**
**
76.0
67.0

+ ** Each of Calfee's bikes have stem and bar spec'd by the dealer prior to shipping
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
CANNONDALE
47
47.8
36.5
650C
int
103
73/78
556
 
51
49.4
37.1
700C
int
90
73/78
565
SLICE ULTIMATE
54
50.9
39.8
700C
int
105
73/78
596
1, 3, & 5
56
52.9
40.7
700C
int
125
73/78
607
58
54.8
41.7
700C
int
145
73/78
623
 
60
57.2
42.5
700C
int
170
73/78
639
           
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
CEEPO
M
516
407
700C
int
110
  
78
588
VIPER
L
535
434
700C
int
130
  
77
620
  
S
456
410
650C
int
100
  
78
582
KATANA
S
493
405
700C
int
90
  
78
592
 
M
513
409
700C
int
110
  
77
598
 
L
534
432
700C
int
130
  
76
611
           
S
493
412
700C
int
90
  
78
599
VENOM
M
517
413
700C
int
110
  
77
598
 
L
534
434
700C
int
130
  
77
625
 
XL
562
437
700C
int
160
  
76
638
           
S
494
405
700C
int
90
  
78
587
STINGER
M
513
409
700C
int
110
  
77
598
 
L
534
420
700C
int
130
  
76
611
           
GRACE
S
494
405
700C
int
90
  
78
587
M
513
409
700C
int
110
  
77
598
           
S
509
400
700C
int
110
  
76
574
MAMBA
M
530
404
700C
int
130
  
75
582
 
L
549
410
700C
int
150
  
74
587
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
CERVELO
48
46.1
38.9
650C
int
110
70x6
Low
75/78
568
 
51
48.2
40.5
700C
int
90
70x6
Low
75/78
586
P3
54
49.8
41.9
700C
int
105
90x6
Low
75/78
609
P4
56
51.6
43.3
700C
int
125
90x6
Low
75/78
626
 
58
53.5
44.5
700C
int
145
110x6
Low
75/78
643
 
61
56.4
45.4
700C
int
175
110x6
Low
75/78
659
           
 
48
46.1
38.9
650C
int
110
70x6
Low
75/78
568
P2
51
48.2
40.5
700C
int
90
70x6
Low
75/78
586
P1
54
51.2
41.8
700C
int
120
90x6
Low
75/78
609
 
56
53.1
42.9
700C
int
140
90x6
Low
75/78
626
 
58
55.0
44.0
700C
int
160
110x6
Low
75/78
643
 
61
57.7
44.7
700C
int
180
110x6
Low
75/78
659
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
ELITE
XS
46.25
35.5
650C
ext
96
**x6
High
78.0
 
 
S
49.0
37.0
650C
ext
115
**x6
High
77.5
 
 
S
48.0
37.0
700C
ext
60
**x6
High
78.0
 
T CLASS
MS
50.5
38.5
700C
ext
80
**x6
High
77.5
 
 
M
52.0
41.0
700C
ext
100
**x6
High
77.0
 
 
L
54.0
44.0
700C
ext
120
**x6
High
76.5
 
 
XL
57.5
44.0
700C
ext
165
**x6
High
76.0
 
 

+ NOTE: Customer specs stem length, but pitch is 6 degrees. Aerobar is Easton, lower profile adjustability is available.
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
FELT
48
46.5
37.0
650C
int
115
60x0
Low
80/78
562
 
50
48.5
39.0
650C
int
130
80x0
Low
80/78
568
DA
52
50.0
40.5
700C
int
100
90x0
Low
80/78
589
B2
54
51.0
41.5
700C
int
110
100x0
Low
78/76
596
S22
56
52.5
42.5
700C
int
125
100x0
Low
78/76
612
S32
58
54.5
45.0
700C
int
145
110x0
Low
78/76
640
 
60
56.0
46.0
700C
int
160
110x0
Low
79/77
657
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
FOCUS
S
480.4
397.7
700C
INT
95
76.3-77
M
505.1
428.7
700C
INT
100
76.3-77
Chrono AMP
L
505.1
453.7
700C
INT
100
76.3-77
S
500.2
366.5
700C
INT
75
Izalco Tria
M
511.0
388.1
700C
INT
75
L
535.2
406.6
700C
INT
75
 XL559.5425.5700CINT   
75
 
47
501.2
390.1
700C
INT
75
Variardo Tria
50
504.3
404.3
700C
INT
100
75
 
53
525.9
409.1
700C
INT
120
75
 
56
553.7
416.9
700C
INT
150
75
 
59
582.4
434.8
700C
INT
180
75
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
FUJI
XS
481
354
700C
int
80
76
 
S
490
372
700C
int
90
76
D6
S/M
500
390
700C
int
100
76
M
509
402
700C
int
110
76
M/L
525
424
700C
int
120
76
L
525
445
700C
int
120
76
 
XL
538
460
700C
int
140
76
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
           
GIANT
XS
50.3
39.7
700C
int
100
90x6
High
78.0
59.0
 
S
51.7
41.0
700C
int
115
90x6
High
78.0
60.7
TRINITY ALLIANCE
M
53.3
42.7
700C
int
130
100x6
High
78.0
62.9
TRINITY
L
54.8
44.9
700C
int
145
110x6
High
78.0
65.0
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
JAMIS
Small
496
405
700C
int
100
*
*
78
 
 
Medium
509
417
700C
int
110
*
*
78
 
XENITH
MedLrg
531
432
700C
int
130
*
*
78
 
T1/T2
Large
556
457
700C
int
160
*
*
78
 
 
51
493
402
700C
int
100
 
high
78
 
 
54
506
415
700C
int
110
 
high
78
 
TRILOGY
56
525
430
700C
int
130
 
high
78
 
 
58
539
447
700C
int
145
 
high
78
 
 
61
553
454
700C
int
160
 
high
78
 
 
51
493
402
700C
int
100
 
high
78
 
 
54
506
415
700C
int
110
 
high
78
 
COMET
56
525
431
700C
int
130
 
high
78
 
 
58
539
447
700C
int
145
 
high
78
 
 
61
554
454
700C
int
160
 
high
78
 
*NOTE: The T1 features high profile Profile T2 Cobras with a 6° stem. The T2 features low profile Easton Attack TT bars with a 10° stem.
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
KESTREL
47
437
399
650C
int
76
90x6
High
78.0
563
 
50
466
409
650C
int
105
90x6
High
77.5
579
 
52
484
415
650C
int
123
90x6
High
77.0
589
AIRFOIL PRO
54
503
426
700C
int
92
100x6
High
76.5
607
 
56
521
431
700C
int
113
100x6
High
76.0
616
 
59
550
439
700C
int
142
110x6
High
76.0
630
           
 
47
467
397
650C
int
105
  
76.5
567
4000
50
525
394
700C
int
115
  
76.5
687
 
525
530
403
700C
int
120
  
76.0
595
 
55
536
418
700C
int
125
  
76.0
609
 
575
541
440
700C
int
130
  
76.0
633
 
595
547
450
700C
int
135
  
76.0
642
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
KUOTA
48
49.6
39.0
700C
int
95
  
79
58.2
 
51
49.6
41.1
700C
int
95
  
79
60.0
KUEEN-K
54
50.5
42.4
700C
int
105
  
79
61.6
 
56
51.5
43.9
700C
int
115
  
79
63.3
 
58
53.4
45.4
700C
int
135
  
79
63.4
           
 
S
49.8
39.6
700C
int
95
90x6
High
76
58.6
 
M
50.8
41.1
700C
int
105
90x6
High
76
60.4
KALIBUR
L
52.3
42.8
700C
int
120
100x6
High
76
61.8
 
XL
54.2
43.9
700C
int
140
1100x6
High
76
63.4
           
 
S
52.7
38.7
700C
int
125
70x6
High
76
58.6
 
M
53.6
40.2
700C
int
135
70x6
High
76
60.4
K-FACTOR
L
55.2
41.9
700C
int
150
90x6
High
76
61.8
 
XL
56.0
43.9
700C
int
160
90x6
High
76
63.4

+ The Kalibur's standard aerobar is Profile Design, hence the "high" profile designation. This model does, however, have HED as an aerobar option, which would be designated as having "low" profile armrests.
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
LEADER
50
50.0
39.0
700C
int
 
*
*
78
57.0
 
54
51.0
42.0
700C
int
 
*
*
78
60.4
LD735TT
57
52.0
43.0
700C
int
 
*
*
78
62.0
 
60
55.0
44.5
700C
int
 
*
*
78
64.5
 
63
58.0
45.5
700C
int
*
*
78
66.0
           
 
50
50.9
39.6
700C
int
 
*
*
78
57.8
LD720TT
54
51.0
42.6
700C
int
 
*
*
78
60.8
LD780TT
57
52.8
43.7
700C
int
 
*
*
78
62.5
 
60
55.7
45.1
700C
int
 
*
*
78
64.7
 
63
58.5
45.9
700C
int
*
*
78
66.4

+ *NOTE: Leader sells their bikes as bare frames only - no fork, headset, or seatpost is included.
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
LOOK
XS-HI
476.0
386.3
700C
int
75
574
 
XS-LO
461.6
390.7
700C
int
75
574
 
S-HI
477.6
403.2
700C
int
75
587
 
S-LO
463.2
407.6
700C
int
75
587
596*
M-HI
523.7
409.8
700C
int
75
600
M-LO
509.3
414.2
700C
int
75
600
L-HI
523.7
425.5
700C
int
75
615
L-LO
509.3
429.9
700C
int
75
615
*The 596 features the integrated monoblade front end. The integrated stem can be positioned in one of two places - either "hi" or "low." These positions, because of the stem mounting position on the Monoblade, affect the stack and reach. We have made stack and reach assumptions based around a parrallel stem (approx. -17deg stem orientation), as unlike a "normal" frame, the bottom of the stem is not at the top of the headset spacer.
           
 
S
522
387
700C
int
125
78
580
 
MD
547
401
700C
int
151
78
600
576
LG
561
429
700C
int
166
78
632
 
XL
580
450
700C
int
186
78
654
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
ORBEA
48
497
375
700C
int
90
74/76/78
 
51
512
395
700C
int
95
74/76/78
ORDU
54
520
409
700C
int
104
74/76/78
ORA
57
530
422
700C
int
111
74/76/78
           
 
XS
506
385
700C
int
95
76
 
S
527
400
700C
int
115
76
ALETTA
M
542
412
700C
int
130
76
 
L
558
429
700C
int
145
76
 
XL
598
429
700C
int
185
76
           
 
XS
509
375
700C
int
100
76
ALETTA DAMA
S
530
380
700C
int
120
76
 
M
546
391
700C
int
135
76
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
PARLEE
S-LO
495
385
700C
int
90
75-80
 
S-HI
514
379
700C
int
110
75-80
TT
M-LO
505
403
700C
int
100
75-80
 
M-HI
524
397
700C
int
120
75-80
 
ML-LO
524
419
700C
int
120
75-80
 
ML-HI
543
413
700C
int
140
75-80
 
L-LO
543
435
700C
int
140
75-80
 
L-HI
562
429
700C
int
160
75-80
 
XL-LO
562
451
700C
int
160
75-80
 
XL-HI
581
445
700C
int
180
75-80
           

+ The Parlee TT comes with two different headtube lengths for each size. Due to negative reach, the different headtube lengths change both the stack and reach of the size. We've designated the shorter headtube as the "LO" sub-size and the taller headtube as the "HI" sub-size.
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
PLANET X
XS
475
380
650C
int
 
S
490
396
700C
int
95
76
570
STEALTH
M
506
407
700C
int
100
76
591
L
523
418
700C
int
120
76
612
XL
536
429
700C
int
130
76
632
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
QR
XS
453
393
650C
int
10
78
572
2011 KILO
S
502
388
700C
int
10
78
577
LUCERO
M
527
403
700C
int
12.5
78
605
SEDUZA
L
568
424
700C
int
16.8
78
639
           
 
S
497
396
650C
int
9
78.5
589
CD0.1
M
497
420
700C
int
9
78.6
613
 
ML
517
438
700C
int
11
78.5
636
 
L
545
452
700C
int
14
78.5
659
           
46
45.0
38.0
650C
int
90
90x5
Low
78.5
555
2010
49
50.0
40.0
700C
int
90
90x5
Low
78.5
592
TEQUILO
52
50.0
42.0
700C
int
90
100x5
Low
78.5
611
AND PRIOR  
55
51.7
43.5
700C
int
110
100x5
Low
78.5
634
 
58
55.0
44.8
700C
int
140
110x5
Low
78.5
657
 
61
57.8
46.1
700C
int
165
110x5
Low
78.5
680
           
2010 DULCE
XS
453
394
650C
int
10
78
572
SEDUZA
S
486
388
700C
int
8.5
77
577
CALIENTE
M
514
407
700C
int
11
77
605
LUCERO AND PRIOR
L
550
430
700C
int
15
76.5
639
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
RIDLEY
XS
490.6
397.7
700C
int
90
76/78.5
585
 
S
501.7
414.9
700C
int
100
76/78.5
598
DEAN
M
520.9
430.1
700C
int
120
76/78.5
618.9
L
535.2
447
700C
int
135
76/78.5
639
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
SPECIALIZED
Small
50.5
38.0
700C
int
100
75x8
*
*
57.5
 
Medium
51.6
39.5
700C
int
110
75x8
*
*
58.9
TRANSITION 2009 (all submodels)
Large
52.6
40.5
700C
int
120
90x8
*
*
60.2
 
XLarge
52.7
42.5
700C
int
120
100x8
*
*
62.1
           
XSmall
487
365
700C
int
90
75x8
*
*
577
Small
505
380
700C
int
100
90x8
*
*
575
 
Medium
516
395
700C
int
110
100x8
*
*
589
TRANSITION 2010 (all submodels)
Large
526
405
700C
int
120
110x8
*
*
602
 
XLarge
542
421
700C
int
135
120x8
*
*
621
XXLarge
554
450
700C
int
150
120x8
*
*
652
           
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
SCOTT
XS
50.4
37.9
700C
int
100
90x8
High
76.0
58.1
 
S
51.5
39.2
700C
int
110
90x8
High
76.0
59.3
PLASMA I & II
M
52.7
40.4
700C
int
120
110x8
High
76.0
60.4
 
L
54.8
41.6
700C
int
140
110x8
High
76.0
61.8
 
XL
58.1
42.7
700C
int
175
120x8
High
76.0
64.0
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
TITANFLEX
S
472
398
650C
ext
110
75-84
576
 
M
472
446
650C
ext
110
75-84
624
AL-Ti
M
516
379
700C
ext
110
72.5-81
564
 
L
536
407
700C
ext
130
72.5-81
599
 
SL
536
432
700C
ext
130
72.5-81
624
 
XL
579
418
700C
ext
175
73-81
623
           
 
M
472
411
650C
ext
110
74-81
589
TRANSITION
M
516
379
700C
ext
110
72.5-81
564
 
L
536
422
700C
ext
130
72.5-81
614
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
TREK
XS*
468
370
650C
int
101
50/10
**
78
549
S
492
390
700C
int
76
60/45
**
78
575
SPEED CONCEPT
M
517
408
700C
int
101
100/45
**
78
598
9-series
L
541
426
700C
int
127
100/45
**
78
624
 
XL
565
445
700C
int
152
100/45
**
78
650


+ * The XS size is available in all models except the SpeedConcept 9.9.
+ ** The SpeedConcept 9-series features an adjustable stack-height aerobar

XS
468
370
650C
int
101
60x7
High
78
549
S
492
390
700C
int
76
70x7
High
78
575
SPEED CONCEPT
M
517
408
700C
int
101
80x7
High
78
598
7-series
L
541
426
700C
int
127
90x7
High
78
624
 
XL
565
445
700C
int
152
90x7
High
78
650
           
 
S
51.5
39.1
700C
int
105
70x12
High
75/77
573.8
EQUINOX TTX (all)
M
52.4
41.9
700C
int
115
70x12
High
75/77
604.1
 
L
56.4
42.9
700C
int
155
70x12
High
75/77
626.1
           
 
50
48.5
38.9
700C
int
78
70x7
High
76
570.3
 
52
50.5
40.0
700C
int
97
80x7
High
76
582.4
EQUINOX 7
54
52.6
41.4
700C
int
116
90x7
High
76
599.1
 
56
54.4
42.0
700C
int
136
100x7
High
76
610.1
 
58
56.4
43.5
700C
int
156
110x7
High
76
631.1
 
60
59.1
44.8
700C
int
186
120x7
High
76
652.6
           
 
47
48.5
38.0
700C
int
7.8
70x7
High
76
560.4
EQUINOX 7 WSD
51
51.8
38.1
700C
int
11.3
80x7
High
76
572.8
 
54
54.8
38.81
700C
int
14.1
90x7
High
76
580.6
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
VALDORA
S
54.6
40.3
700C
int
130
 
 
76
59.6
PHX
M
56.6
42.7
700C
int
150
 
 
76
62.1
 
L
58.7
44.6
700C
int
170
 
 
76
64.0
           
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
 
Size
Stack
Reach
Wheel
HS
HT
Stem
Hbars
SA
Ft/Ctr
WILIER
S
498.6
401.5
700C
int
90
 
 
76.5
585.0
CENTO CRONO
M
523.0
438.0
700C
int
110
 
 
77
618.0
 
L
538.5
469.6
700C
int
125
 
 
78
656.0
           
+
diff --git a/site/articles/include_subscribe.html b/site/articles/include_subscribe.html new file mode 120000 index 0000000..3fe8a6c --- /dev/null +++ b/site/articles/include_subscribe.html @@ -0,0 +1 @@ +../common/templates/include_subscribe.html \ No newline at end of file diff --git a/site/articles/include_widgets.html b/site/articles/include_widgets.html new file mode 100644 index 0000000..93d61d7 --- /dev/null +++ b/site/articles/include_widgets.html @@ -0,0 +1,14 @@ +<%~if not widget_end%><%set widget_end = $widgets_loop.length%><%endif%> +<%~loop widgets_loop%> + <%~if row_num >= $widget_start%> + <%~if Type%> + <%include _widget_automated.html%> + <%~else%> + <%include _widget_manual.html%> + <%~endif%> + <%~if row_num < $widget_end or $widgets_loop.length == 1%> +
+ <%~endif%> + <%~endif%> + <%~if row_num == $widget_end%><%lastloop%><%endif%> +<%~endloop%> diff --git a/site/articles/link.html b/site/articles/link.html new file mode 100644 index 0000000..db50eae --- /dev/null +++ b/site/articles/link.html @@ -0,0 +1,59 @@ +<%~rewrite_detail_url%> +<%~if list_view eq 'latest'%> +
+
+ <%~if thumbnail_url%> + + <%else%> + + <%~endif%> +
+
+ +

<%escape_html Title%><%if Link_Type eq 'video'%><%elsif Link_Type eq 'photo'%><%endif%>

+
+
+<%~elsif list_view eq 'featured'%> + +<%if row_index == 2%>
<%endif%> +<%~else%> +
+
+ <%~if thumbnail_url%> + + <%~else%> + + <%~endif%> +
+
+ <%~if show_category%><%endif%> +

<%escape_html Title%><%if Link_Type eq 'video'%><%elsif Link_Type eq 'photo'%><%endif%>

+ <%~if list_view ne 'column'%> +
+
+ <%~else%> + + <%~endif%> +
+
+<%~endif%> diff --git a/site/articles/local/category.html b/site/articles/local/category.html new file mode 100644 index 0000000..e274a70 --- /dev/null +++ b/site/articles/local/category.html @@ -0,0 +1,48 @@ + + + + <%site_title%> +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> +
+ <%include include_header.html%> + +
+
+ <%include include_breadcrumb.html%> + <%~if not links_loop.length%> +
+ +
+
+ + +
+

<%Name%>

+ <%~loop category_loop%> + <%include include_category.html%> + <%~endloop%> +
+
+
+
+ <%~else%> + <%include subcategory.html%> + <%~endif%> + +
+ +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> diff --git a/site/articles/local/coaching.html b/site/articles/local/coaching.html new file mode 100644 index 0000000..20af5e3 --- /dev/null +++ b/site/articles/local/coaching.html @@ -0,0 +1,69 @@ + + + + <%site_title%> +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> + +
+ <%include include_header.html%> + +
+
+ <%~set title_loop = Links::Build::build('title', 'Slowtwitch Coaching')%> + <%include include_breadcrumb.html%> + +
+ + +
+
+
+

Slowtwitch Coaching

+ +

Slowtwitch Coaching Certification Clinics are designed for the multisport or individual sport coach looking to take his or her knowledge and coaching business to the next level. By combining interactive discussions and presentations with actual hands-on coaching instruction, the Slowtwitch Coaching Certification Clinics go beyond the traditional classroom setting to provide coaches with real, practical training that directly translates to their coaching careers.

+

Slowtwitch currently offers the following coaching certifications:

+ +

Click on the links above to learn more about each clinic and see a sample schedule with descriptions of each session. +

You can find a list of Slowtwitch Certified Coaches here. +

+ +
+
+
+
+ +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/articles/local/detailed.html b/site/articles/local/detailed.html new file mode 100644 index 0000000..acbc90f --- /dev/null +++ b/site/articles/local/detailed.html @@ -0,0 +1,238 @@ +<%~set detailed_url = get_detailed_url($ID)%> +<%~Links::Utils::load_reviews($ID, $detailed_max_reviews)~%> +<%~Plugins::SlideShow::generate_paths($ID)%> +<%~convert_nl_br%> +<%old_detailed_url($detailed_url)%> + + + + <%Title%> - <%site_title_short%> + <%--Facebook Meta Tags--%> + + + + + + + + + <%--Twitter Meta Tags--%> + + + + + + <%~if Image1_thumbnail_path%> + + + + <%~elsif Thumbnail_URL and Thumbnail_URL ne 'http://' %> + + + + <%else%> + <%~Plugins::ConvertVideo::get_file_path($ID, "thumbnail_file_field") %> + <%~if thumbnail_file_field_path%> + + + + <%~endif%> + <%~endif%> + <%~include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> + +
+ <%include include_header.html%> + +
+
+ <%include include_breadcrumb.html%> + +
+ + +
+
+
+

<%Title%>

+ + +
+
+
+ + <%~if Link_Type eq 'article'%> + <%~include include_detailed_article.html%> + <%~elsif Link_Type eq 'photo' %> + <%~if slideshow%> + <%~include include_detailed_slideshow.html%> + <%~elsif is_old_article($Add_Date_orig)%> + <%~include include_detailed_old_photo.html%> + <%else%> + <%~include include_detailed_photo.html%> + <%~endif%> + <%~else%> + <%~include include_detailed_video.html%> + <%~endif%> + +
+ <%~if user.Status eq 'Administrator'%> + EDIT THIS ARTICLE + EDIT FEATURED LINKS + <%~endif%> + <%if user.Status eq 'Editor' AND user.Status neq 'Administrator'%>EDIT THIS ARTICLE<%endif%> + <%if user.Status eq 'Administrator'%>Log Out<%endif%> + <%if config.bookmark_enabled%>Bookmark It<%endif%> + <%if isLinkOwner%>Edit this link<%endif%> + <%if paymentsEnabled%><%if isUnpaid or isFree%>New Payment<%else%>Renewal Payment<%endif%><%endif%> +
+ + <%include include_share.html%> + <%include include_fb_comments.html%> +
+
+
+ +
+
+
+
+ + + +
+ +
+ + + +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/articles/local/home.html b/site/articles/local/home.html new file mode 100644 index 0000000..aa5bd13 --- /dev/null +++ b/site/articles/local/home.html @@ -0,0 +1,143 @@ + + + + <%site_title%> +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> + + +
+ <%include include_header.html%> + +
+
+ + <%~if $config.featured_articles%><%set featured_articles_loop = Plugins::UI::fetch_links(ids => $config.featured_articles)%><%endif%> + <%~if featured_articles_loop.length%> + +
+ <%~endif%> + + <%~set widgets_loop = Plugins::UI::fetch_widgets('home')%> +
+ +
+
+ +
+ <%~set row_index = 0%> + <%~set latest_news = Plugins::UI::fetch_links(max_hits => 21)%> + <%~set list_view = 'latest'%> + <%~if latest_news.length%> + <%~loop latest_news%> + + <%~if row_index == 10%> +
+
+ <%~set adsize = '300x250_1'%> + <%include include_ad.html%> + + <%~endif%> + + <%~set local_id = $ID %> + <%~set is_featured = 0 %> + <%~loop featured_articles_loop%> + <%~if $local_id == $ID %> + <%~set is_featured = 1 %> + <%~endif%> + <%~endloop%> + <%~if not is_featured%> + <%include link.html%> + <%~set row_index += 1%> + <%~endif%> + <%~endloop%> + <%~endif%> +
+ +
+ +
+ +
+
+
+ + +
+ <%~set adsize = '970x250'%> + <%include include_ad.html%> +
+ + +
+ +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/articles/local/include_ad.html b/site/articles/local/include_ad.html new file mode 100644 index 0000000..e413996 --- /dev/null +++ b/site/articles/local/include_ad.html @@ -0,0 +1,28 @@ +<%~if adsize eq '300x600'%> +
+ +
+<%~elsif adsize eq '970x250'%> +
+ +
+<%~elsif adsize eq '728x90' and $ad_728x90%> +
+ +
+<%~elsif adsize eq '300x250_1'%> + +<%~elsif adsize eq '300x250'%> + +<%~endif%> diff --git a/site/articles/local/include_common_head.html b/site/articles/local/include_common_head.html new file mode 100644 index 0000000..daa8131 --- /dev/null +++ b/site/articles/local/include_common_head.html @@ -0,0 +1,2 @@ +<%~set minify = $config.minify || 0%> +<%include include_global_head.html%> \ No newline at end of file diff --git a/site/articles/local/include_featured_articles.html b/site/articles/local/include_featured_articles.html new file mode 100644 index 0000000..f09524f --- /dev/null +++ b/site/articles/local/include_featured_articles.html @@ -0,0 +1,21 @@ + diff --git a/site/articles/local/include_featured_photo_video.html b/site/articles/local/include_featured_photo_video.html new file mode 100644 index 0000000..76a8730 --- /dev/null +++ b/site/articles/local/include_featured_photo_video.html @@ -0,0 +1,7 @@ + +
+<%~set adsize = '970x250'%> +<%include include_ad.html%> +
+ +
\ No newline at end of file diff --git a/site/articles/local/include_subscribe.html b/site/articles/local/include_subscribe.html new file mode 100644 index 0000000..ce755f8 --- /dev/null +++ b/site/articles/local/include_subscribe.html @@ -0,0 +1,28 @@ + + + +
+
+
+
+ + + +
+ +
+ + +
+ +
+
+
+
+ + diff --git a/site/articles/local/podcast.html b/site/articles/local/podcast.html new file mode 100644 index 0000000..d577c29 --- /dev/null +++ b/site/articles/local/podcast.html @@ -0,0 +1,84 @@ + + + + <%site_title%> +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> + +
+ <%include include_header.html%> + +
+
+ <%~set title_loop = Links::Build::build('title', 'Slowtwitch Podcast')%> + <%include include_breadcrumb.html%> + +
+ + +
+
+
+

Slowtwitch Podcast

+ + Hosted by Ryan & Eric The Slowtwitch Podcast is the latest addition to our coverage. + +

Guests or not each episode will be wide-ranging conversations while staying true to the ethos of Slowtwitch: fiercely independent and authentic. + +

Episodes will release weekly on Thursdays and be available on your favorite streaming services. + +

Find us on: + +
+ +
+ + +
+ <%~loop category_loop%> + <%include include_category.html%> + <%~endloop%> +
+
+ +
+
+
+
+ +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/articles/local/subcategory.html b/site/articles/local/subcategory.html new file mode 100644 index 0000000..631ffba --- /dev/null +++ b/site/articles/local/subcategory.html @@ -0,0 +1,144 @@ +<%~set widgets_loop = Plugins::UI::fetch_widgets('subcategory', $ID)%> +<%~set widget_start = 1%> +<%~set widget_end = 3%> + +<%~if paging.current_page == 1 or not $paging%> + +
+<%~endif%> + +<%~set list_view = ''%> +<%~set num_links = $links_loop.length%> +<%~set num_page_break = 9%> + +
+ +
+
+
+ <%~if paging.current_page > 1%> +
+

<%Name%>

+ <%include include_paging.html%> +
+ <%~endif%> + <%~loop links_loop%> + <%if skip_top_three and $row_num <= $max_hits%><%nextloop%><%endif%> + <%~rewrite_detail_url%> + <%~set thumbnail_url = Plugins::UI::fetch_thumbnail($loop_value)%> + <%include link.html%> + <%~if row_num == $num_page_break%><%lastloop%><%endif%> + <%~endloop%> +
+
+
+
+
+
+
+ +<%~include include_featured_photo_video.html%> + +<%~if num_links > $num_page_break%> +
+ +
+
+
+ <%~loop links_loop%> + <%~if row_num <= $num_page_break%><%nextloop%><%endif%> + <%~rewrite_detail_url%> + <%~set thumbnail_url = Plugins::UI::fetch_thumbnail($loop_value)%> + <%include link.html%> + <%~endloop%> +
+ <%~set page_button = 'page_button2'%> +
+ <%include include_paging.html%> +
+
+
+
+
+
+<%~endif%> diff --git a/site/articles/login.html b/site/articles/login.html new file mode 100644 index 0000000..1d2b53f --- /dev/null +++ b/site/articles/login.html @@ -0,0 +1,74 @@ + +<%~if logout and error%> + <%~set message = $error%> + <%~set error = ''%> +<%~endif%> +<%~if url and not error%> + <%~set error = 'You must first login before you can access that.'%> +<%~endif%> + + + <%site_title%>: User Login +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> + +
+ <%include include_header.html%> +
+
+ + +
<%-- start content--%> +

User Login

+ +
+ + <%if url%><%endif%> +
+
+
Username
+
+
+ +
+
+
+
Password
+
+
+ +
+
+ <%~if config.user_sessions eq Cookies%> +
+
Remember me
+
+
+ +
+
+ <%~endif%> +
+
+
+
+ +

If you've forgotten your password, we can e-mail it to you.

+
+
+
+
+
<%-- end content --%> +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/articles/login_email.html b/site/articles/login_email.html new file mode 100644 index 0000000..f72265f --- /dev/null +++ b/site/articles/login_email.html @@ -0,0 +1,48 @@ + + + + <%site_title%>: Forgotten Password +<%include include_common_head.html%> + + +
+ <%include include_header.html%> +
+
+ + +
<%-- start content--%> +

Forgot your password?

+

Simply enter your e-mail address that you signed up with and we will e-mail you your password.

+ +
+ + <%if url%><%endif%> + +
+
+
E-mail Address
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
<%-- end content --%> +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/articles/login_success.html b/site/articles/login_success.html new file mode 100644 index 0000000..f0b5b9e --- /dev/null +++ b/site/articles/login_success.html @@ -0,0 +1,32 @@ + + + + <%site_title%>: Logged In +<%include include_common_head.html%> + + +
+ <%include include_header.html%> +
+
+ + +
<%-- start content--%> +

Logged In

+

You have been successfully logged in.<%--site_title--%>

+ + <%if not d and Links::Utils::is_editor%> +

+ Enter the editor system. +

+ <%endif%> +
<%-- end content --%> +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/articles/modify.html b/site/articles/modify.html new file mode 100644 index 0000000..9a5ee9c --- /dev/null +++ b/site/articles/modify.html @@ -0,0 +1,95 @@ + + + + <%site_title%>: Modify a Link +<%-- + + + +--%> +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> +
+ <%include include_header.html%> +
+
+ + +
<%-- start content--%> +

Modify a Link

+ <%~ set megabytes = 1048576 ~%> + <%~ set image_cfg = Links::Plugins::get_plugin_user_cfg('SlideShow') ~%> + <%~ set image_size = image_cfg.max_upload_size / $megabytes ~%> + <%~ set image_size = round($image_size) ~%> + <%~ set video_max_size = Plugins::ConvertVideo::get_video_max_size() ~%> + <%~ set video_size = $video_max_size / $megabytes ~%> + <%~ set video_size = round($video_size) ~%> +

Note: image files must be smaller than <% if image_size %><% image_size %><%else%>1<%endif%>MB and video files must be smaller than <% if video_size %><% video_size %><%else%>1<%endif%>MB.

+ +
+ + <%~if LinkID%> + + + <%~else%> + <%~if config.db_gen_category_list == 2%> + + + + <%~endif%> +

+ Enter the new information (all of it, not just the changes) below: +

+ <%~endif%> + +
+
<%-- end content --%> +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> +<%~include include_form.js%> + diff --git a/site/articles/modify_select.html b/site/articles/modify_select.html new file mode 100644 index 0000000..4591f69 --- /dev/null +++ b/site/articles/modify_select.html @@ -0,0 +1,46 @@ +<%~ set title = "Modify a Link" %> + + + + <%site_title%>: <%title%> +<%include include_common_head.html%> + + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> +
+ <%include include_header.html%> +
+
+ + +
<%-- start content--%> +

<% title %>

+

+ Please select which link you would like to modify: +

+ + <%if paging.num_hits%>
<%Links::Utils::paging()%>
<%endif%> + +
+ <%~loop link_results_loop%> + disabled="disabled"<%endif%> /> + <%include link.html%> + <%~endloop%> + +
+ + <%if paging.num_hits%>
<%Links::Utils::paging(button_id => 'paging_button2')%>
<%endif%> + +
<%-- end content --%> +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/articles/modify_success.html b/site/articles/modify_success.html new file mode 100644 index 0000000..b8c2790 --- /dev/null +++ b/site/articles/modify_success.html @@ -0,0 +1,72 @@ + +<%~Plugins::UI::update_featured_links('article', $ID, $in.featured_article)%> +<%~Plugins::UI::update_featured_links('photo', $ID, $in.featured_photo)%> + + + <%site_title%>: Link Modified +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> +
+ <%include include_header.html%> +
+
+ + +
<%-- start content--%> +

Link Modified

+ +

+ <%~if payment_term eq free%> + Your link has been changed to a free link: + <%~else%> + We have received your request to modify the link as follows: + <%~endif%> +

+ +
+ +
<%Title%>
+
+
+ +
+ <%~if Category_loop.length > 1%> +
    <%loop Category_loop%>
  • <%loop_value%>
  • <%endloop%>
+ <%~else%> + <%Category%> + <%~endif%> +
+
+
+ +
<%if Link_Type eq 'photo'%>Photo Gallery<%elseif Link_Type eq 'video'%>Video<%else%>Article<%endif%>
+
+
+ +
<%escape_html Description%>
+
+
+ +
<%escape_html Contact_Name%>
+
+
+ +
<%escape_html Contact_Email%>
+
+ + <%if not config.user_direct_mod and payment_term ne free%>

Thank you! We will send you an e-mail once your link has been validated.

<%endif%> + +
<%-- end content --%> +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/articles/photos.html b/site/articles/photos.html new file mode 100644 index 0000000..690ede8 --- /dev/null +++ b/site/articles/photos.html @@ -0,0 +1,97 @@ + +<%convert_nl_br%> + + + <%site_title%> +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> +
+ <%include include_header.html%> + +
+
+ <%~if in.video%> + <%~set media = Plugins::UI::fetch_links(link_type => 'video', max_hits => $config.search_maxhits)%> + <%~set title_loop = Links::Build::build('title', 'Videos')%> + <%~else%> + <%~set media = Plugins::UI::fetch_links(link_type => 'photo', max_hits => $config.search_maxhits)%> + <%~set title_loop = Links::Build::build('title', 'Photo Galleries')%> + <%~endif%> + <%include include_breadcrumb.html%> + <%~set paging = $media.paging%> + <%~set featured_articles_loop = $media.features%> + + <%~if paging and paging.current_page == 1%> + + +
+ <%~endif%> + + + <%~set list_view = ''%> +
+ + +
+
+
+ <%~if paging.current_page > 1%> +

<%if in.video%>Videos<%else%>Photos<%endif%>

+
+ <%include include_paging.html%> +
+ <%~endif%> + + <%~set show_category = 1%> + <%~loop media.loop%> + <%include link.html%> + <%~if row_num == $num_page_break%><%lastloop%><%endif%> + <%~endloop%> + <%~if paging.max_hits < $paging.num_hits%> +
+ <%~set page_button = 'page_button2'%> + <%include include_paging.html%> +
+ <%~endif%> +
+ +
+
+
+
+ +
+ +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> diff --git a/site/articles/races.html b/site/articles/races.html new file mode 100644 index 0000000..e232238 --- /dev/null +++ b/site/articles/races.html @@ -0,0 +1,63 @@ + + + + <%site_title%> +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> +
+ <%include include_header.html%> + +
+
+ <%set title_loop = Links::Build::build('title', 'Races')%> + <%include include_breadcrumb.html%> +
+ +
+
+
+

Races

+ +
+
+ +
+
+ Race Calendar +
+ See Slowtwitch.com's North American Multisport Race Calendar along with features providing participants a greater sense of community. +
+
+ +
+ + <%~set category_loop = Plugins::UI::fetch_categories(2)%> + <%~loop category_loop%> + <%include include_category.html%> + <%~endloop%> +
+
+
+
+
+ +
+ +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> diff --git a/site/articles/search.html b/site/articles/search.html new file mode 100644 index 0000000..e7e1cb9 --- /dev/null +++ b/site/articles/search.html @@ -0,0 +1,226 @@ + + + + <%site_title%>: Search Form +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> +
+ <%include include_header.html%> + +
+
+ <%include include_breadcrumb.html%> + +
+ +
+
+
+

Search Form

+
+ <%~if error%>

<%error%><%endif%> +

SEARCH SLOWTWITCH.COM KNOWLEDGE BASE

+
+
+
+
Search
+
+
+ +
+
+ +
+
Search Options
+
+
+ checked="checked" <%endif%>class="radio" /> + checked="checked" <%endif%>class="radio" /> +
+
+ +
+
+
+
+ checked="checked" <%endif%>class="radio" /> + checked="checked" <%endif%>class="radio" /> +
+
+ +
+
Links per page
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +

Search Forums

+
+ <%hidden_form || ''%> + +
+
+
Search
+
+
+ +
+
+
+
Search String
+
+
+ value="<%search_string%>"<%endif%> class="txt midtext" /> +
+
+
+
Type of search
+
+
+ +
+
+
+
Fields to search
+
+
+ +
+
+
+
Show posts from the last
+
+
+ +
+
+
+
Only show posts made by
+
+
+ value="<%search_user_username%>"<%endif%> class="txt shotext" /> +
+
+
+
Only show
+
+
+ +
+
+
+
Sort posts by
+
+
+ +
+
+
+
Number of results per page
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+ +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> diff --git a/site/articles/search_results.html b/site/articles/search_results.html new file mode 100644 index 0000000..11890b4 --- /dev/null +++ b/site/articles/search_results.html @@ -0,0 +1,78 @@ + + + + <%site_title%>: Search Results +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> +
+ <%include include_header.html%> + +
+
+ <%include include_breadcrumb.html%> +
+ +
+
+
+

Search Results

+ Advanced Search +
+

Your search<%if query%> for <%if highlight%><%set equery = escape_html $query%><%Links::Tools::highlight($equery, $equery)%><%else%><%escape_html query%><%endif%><%endif%> returned <%cat_hits%> categor<%if cat_hits != 1%>ies<%else%>y<%endif%> and <%link_hits%> link<%if link_hits != 1%>s<%endif%>

+ <%if category_results_loop.length~%> + +

Categories

+
    + <%~loop category_results_loop%> + <%~set formatted_title = Links::Utils::format_title($title_loop, separator => ' > ', no_escape_separator => $no_escape_category_separator, include_home => 0, link_type => 1)%> +
  • <%if highlight and query%><%Links::Tools::highlight($formatted_title, $query)%><%else%><%formatted_title%><%endif%>
  • + <%~endloop%> +
+ <%~endif%> + + <%~if link_results_loop.length~%> + <%~if paging.num_hits > $paging.max_hits%> +
+

Links

+ <%~set page_button = 'page_button2'%> + <%include include_paging.html%> +
+ <%~endif%> + <%~set search_results_categories = get_links_categories($link_results_loop) %> + <%loop link_results_loop~%> + <%if title_loop.length%> + <%~set formatted_title = Links::Utils::format_title($title_loop, separator => $category_separator, no_escape_separator => $no_escape_category_separator, include_home => 0, link_type => 1)%> +

<%if highlight and query%><%Links::Tools::highlight($formatted_title, $query)%><%else%><%formatted_title%><%endif%>

+ <%~endif%> + <%~rewrite_detail_url%> + <%~set thumbnail_url = Plugins::UI::fetch_thumbnail($loop_value)%> + <%include link.html%> + <%~endloop%> + <%~if paging.num_hits > $paging.max_hits%> +
+ <%~set page_button = 'page_button2'%> + <%include include_paging.html%> +
+ <%~endif%> + <%~endif%> +
+
+
+
+ +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> diff --git a/site/articles/stackreach.html b/site/articles/stackreach.html new file mode 100644 index 0000000..77890c3 --- /dev/null +++ b/site/articles/stackreach.html @@ -0,0 +1,54 @@ + + + + <%site_title%>: Stack & Reach Database +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> + +
+ <%include include_header.html%> + +
+
+ <%~set title_loop = Links::Build::build('title', 'Slowtwitch Stack & Reach')%> + <%include include_breadcrumb.html%> + +
+ + +
+
+
+

Stack & Reach Database

+ <%include include_stackreach_content.html%> +
+ +
+
+
+
+ +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + + diff --git a/site/articles/subcategory.html b/site/articles/subcategory.html new file mode 100644 index 0000000..1818b30 --- /dev/null +++ b/site/articles/subcategory.html @@ -0,0 +1,120 @@ +<%~set widgets_loop = Plugins::UI::fetch_widgets('subcategory', $ID)%> +<%~set widget_start = 1%> +<%~set widget_end = 3%> + +<%~if paging.current_page == 1 or not $paging%> + +
+<%~endif%> + +<%~set list_view = ''%> +<%~set num_links = $links_loop.length%> +<%~set num_page_break = 9%> + +
+ +
+
+
+ <%~if paging.current_page > 1%> +
+

<%Name%>

+ <%include include_paging.html%> +
+ <%~endif%> + <%~loop links_loop%> + <%if skip_top_three and $row_num <= $max_hits%><%nextloop%><%endif%> + <%~rewrite_detail_url%> + <%~set thumbnail_url = Plugins::UI::fetch_thumbnail($loop_value)%> + <%include link.html%> + <%~if row_num == $num_page_break%><%lastloop%><%endif%> + <%~endloop%> +
+
+
+
+
+
+
+ +<%~include include_featured_photo_video.html%> + +<%~if num_links > $num_page_break%> +
+ +
+
+
+ <%~loop links_loop%> + <%~if row_num <= $num_page_break%><%nextloop%><%endif%> + <%~rewrite_detail_url%> + <%~set thumbnail_url = Plugins::UI::fetch_thumbnail($loop_value)%> + <%include link.html%> + <%~endloop%> +
+ <%~set page_button = 'page_button2'%> +
+ <%include include_paging.html%> +
+
+
+
+
+
+<%~endif%> diff --git a/site/articles/tag.html b/site/articles/tag.html new file mode 100644 index 0000000..2dceda3 --- /dev/null +++ b/site/articles/tag.html @@ -0,0 +1,103 @@ + +<%convert_nl_br%> + + + <%site_title%> +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> +
+ <%include include_header.html%> + +
+
+ <%~set tag = $in.tag%> + <%~if tag ieq 'swim'%><%set tag_title = 'Swim'%> + <%~elsif tag ieq 'bike'%><%set tag_title = 'Bike'%> + <%~else%><%set tag_title = 'Run'%><%endif%> + + <%~set res = Plugins::UI::fetch_links(tag => $tag, max_hits => $config.search_maxhits, url => "$tag_title/" )%> + + <%~set title_loop = Links::Build::build('title', $tag_title)%> + <%include include_breadcrumb.html%> + <%~set paging = $res.paging%> + <%~set featured_articles_loop = $res.features%> + <%unless featured_articles_loop%> +

No article found

+ <%endif%> + + <%~if paging and paging.current_page == 1%> + + + <%~endif%> + + + <%~if paging.num_hits > 3%> + <%if paging.current_page == 1%>
<%endif%> + <%~set list_view = ''%> +
+ + +
+
+
+ <%~if paging.current_page > 1%> +

<%tag_title%>

+
+ <%include include_paging.html%> +
+ <%~endif%> + + <%~set show_category = 1%> + <%~loop res.loop%> + <%include link.html%> + <%~if row_num == $num_page_break%><%lastloop%><%endif%> + <%~endloop%> + <%~if paging.max_hits < $paging.num_hits%> +
+ <%~set page_button = 'page_button2'%> + <%include include_paging.html%> +
+ <%~endif%> +
+ +
+
+
+
+ <%~endif%> + +
+ +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> diff --git a/site/coaches/rating/styles/rating.css b/site/coaches/rating/styles/rating.css new file mode 100644 index 0000000..eec6d79 --- /dev/null +++ b/site/coaches/rating/styles/rating.css @@ -0,0 +1,102 @@ +/* star rating code - use lists because its more semantic */ +/* No javascript required */ +/* all the stars are contained in one matrix to solve rollover problems with delay */ +/* the background position is just shifted to reveal the correct image. */ +/* the images are 16px by 16px and the background position will be shifted in negative 16px increments */ +/* key: B=Blank : O=Orange : G = Green * / +/*..... The Matrix ....... */ +/* colours ....Background position */ +/* B B B B B - (0 0)*/ +/* G B B B B - (0 -16px)*/ +/* G G B B B - (0 -32px)*/ +/* G G G B B - (0 -48px)*/ +/* G G G G B - (0 -64px)*/ +/* G G G G G - (0 -80px)*/ +/* O B B B B - (0 -96px)*/ +/* O O B B B - (0 -112px)*/ +/* O O O B B - (0 -128px)*/ +/* O O O O B - (0 -144px)*/ +/* O O O O O - (0 -160px)*/ + + +/* the default rating is placed as a background image in the ul */ +/* use the background position according to the table above to display the required images*/ +.rating{ + width:80px; + height:16px; + margin:0px 0px 20px 0px; + padding:0; + list-style:none; + float: left; + position:relative; + background: url(../images/star-matrix.gif) no-repeat 0 0; +} +ul.rating li { + cursor: pointer; + /*ie5 mac doesn't like it if the list is floated\*/ + float:left; + /* end hide*/ + text-indent:-999em; +} +ul.rating li a { + position:absolute; + left:0; + top:0; + width:16px; + height:16px; + text-decoration:none; + z-index: 200; +} +ul.rating li.one a {left:0} +ul.rating li.two a {left:16px;} +ul.rating li.three a {left:32px;} +ul.rating li.four a {left:48px;} +ul.rating li.five a {left:64px;} +ul.rating li a:hover { + z-index:2; + width:80px; + height:16px; + overflow:hidden; + left:0; + background: url(../images/star-matrix.gif) no-repeat 0 0 +} +ul.rating li.one a:hover {background-position:0 -96px;} +ul.rating li.two a:hover {background-position:0 -112px;} +ul.rating li.three a:hover {background-position:0 -128px} +ul.rating li.four a:hover {background-position:0 -144px} +ul.rating li.five a:hover {background-position:0 -160px} + +/* this is used to remove the hover affect */ +/* use the background position according to the table above to display the required images*/ +.rated{ + width:80px; + height:16px; + margin:0px 0px 20px 0px; + padding:0; + list-style:none; + float: left; + position:relative; + background: url(../images/star-matrix.gif) no-repeat 0 0; +} +ul.rated li { + cursor: pointer; + /*ie5 mac doesn't like it if the list is floated\*/ + float:left; + /* end hide*/ + text-indent:-999em; +} +ul.rated li.one a {left:0} +ul.rated li.two a {left:16px;} +ul.rated li.three a {left:32px;} +ul.rated li.four a {left:48px;} +ul.rated li.five a {left:64px;} + +/* add these classes to the ul to effect the change to the correct number of stars */ +.nostar {background-position:0 0} +.onestar {background-position:0 -16px} +.twostar {background-position:0 -32px} +.threestar {background-position:0 -48px} +.fourstar {background-position:0 -64px} +.fivestar {background-position:0 -80px} +/* end rating code */ +h3{margin:0 0 2px 0;font-size:110%} \ No newline at end of file diff --git a/site/coaches/styles.css b/site/coaches/styles.css new file mode 100644 index 0000000..d2bd5d1 --- /dev/null +++ b/site/coaches/styles.css @@ -0,0 +1,545 @@ +/* Defaults */ +body { +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +color: #000; +padding: 0; +margin: 15px 0 5px; +} + +.indent { +margin-left: 10px; +} + +.indentlarge { +margin-left: 30px; +} + +.indenthuge { +margin-left: 50px; +} + +.rightside { +text-align: right; +} + +form { +margin: 0; +padding: 0; +} + +/* Links +a:link { +color: #000099; +text-decoration : none; +padding : 0; +margin : 0; +} +a:visited { +color: #000099; +text-decoration : none; +} +a:hover { +color: #000000; +text-decoration : underline; +} +a:active { +color: #999999; +text-decoration : none; +}*/ + +.modify{ +text-decoration: none; +color: #FFFFFF; +font-weight: bold; +font-size: 14pt; +} + +.modify:hover{ +text-decoration: underline; +} + +/* Outer layer */ + +#outer { +text-align: left; +background: #ffffff; +color: inherit; +/**border: 1px solid #000000;**/ +margin: 0 auto; +padding: 0; +} + +/* Header */ + +.header { +background: #fcce32; +} + +.logolink { +padding: 6px 3px; +} + + .logolink a { +background: inherit; +} + +.hdr-links { +width: 230px; +text-align: center; +font-size : 10px; +padding : 0; +margin: 0; +} + +a.hdrnav:link { +color: #000099; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +} +a.hdrnav:visited { +color: #000099; +background : inherit; +text-decoration : none; +} +a.hdrnav:hover { +color: #000; +background : #fff; +text-decoration : underline; +} +a.hdrnav:active { +color: #999; +background : inherit; +text-decoration : none; +} + +.hdr-links .access { +text-decoration : underline; +color : #000; +background : inherit; +} + + +/* Top Nav Bar */ + +.top-nav { +height: 25px; +background: #c5c6ac; +color: inherit; +border: solid #000000; +border-width: 1px 0; +} +.top-nav-inner { +padding: 5px 10px; +} +.top-nav-inner a:link { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: none; +} +.top-nav-inner a:visited { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: none; +} +.top-nav-inner a:hover { +background: inherit; +color: #555555; +font-weight: bold; +text-decoration: underline; +} +.top-nav-inner a:active { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: underline; +} + + + +/* Image Bar */ + +.image-bar { +clear:both; +} + +.title-img { +border: 1px solid #000; +color: #ffffff; +background: #343234; +padding: 0 0 0 10px; +} + +.section-title { +font-size: 16px; +font-weight: bold; +font-family: verdana, arial, tahoma, sans-serif; +} + +.graphic-img { +border: 1px solid #000000; +} + +.blank-img { +border: 1px solid #000000; +background: #fcce32; +} + +.blank-img-ads { +border: 1px solid #000000; +background: #fcce32; +} + + +/* Content Section */ + +/*.content { +padding: 0 4px 4px 4px; +margin: 0; +}*/ + +.left-col { +border: 1px solid #000000; +background: #fcce32; +width: 145px; +} + +.left-nav { +margin: 3px; +} + +.statenav { + margin: 4px; +} + +.lnav-hdr { +background: #c5c6ac; +color: #000000; +font-family: verdana, tahoma, arial, sans-serif; +font-size: 11px; +font-weight: bold; +border: 1px solid #000000; +margin-top: 16px; +padding: 2px 5px; +} + +.left-nav p { +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +line-height: 14px; +margin:5px; +} + +.left-nav .access { +text-decoration : underline; +color : #000000; +background : inherit; +} + +a.lhdr:link { +color: #000000; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +font-weight: bold; +} +a.lhdr:visited { +color: #000000; +background : inherit; +text-decoration : none; +font-size: 10px; +font-weight: bold; +} +a.lhdr:hover { +color: #000; +background : #fcfefc; +text-decoration : underline; +} +a.lhdr:active { +color: #555; +background : inherit; +text-decoration : none; +} + +input.nobutton { + background-color: #0A50A1; + font-weight: bold; + font-size: 9px; + color: white; +} + +input.nobutton:visited { + background-color: #fcce32; + font-weight: bold; + font-size: 9px; + color: white; +} + +input.nobutton:active { + background-color: #fcce32 + color: white; + font-weight: bold; + font-size: 9px; +} + +a.nav:link { +color: #000099; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +} +a.nav:visited { +color: #000099; +background : inherit; +text-decoration : none; +} +a.nav:hover { +color: #000; +background : #fcfefc; +text-decoration : underline; +} +a.nav:active { +color: #999; +background : inherit; +text-decoration : none; +} + + + +/* Main content box */ +.content-box { +padding: 0; +margin: 0; +} + +/* right content box */ +.rhcol { +float: right; +width: 160px; +margin: 0 0 10px 15px; +background: #fcfefc; +color: #000000; +} +.rhcont { +border-left: 1px solid #000000; +border-bottom:3px solid #000000; +} +.rnav-hdr { +background: #c5c6ac; +color: #000000; +font-family: verdana, tahoma, arial, sans-serif; +font-size: 12px; +font-weight:bold; +border-bottom: 1px solid #000; +border-left: 2px solid #000000; +margin: 3px 0; +padding: 2px; +} + +/* main content area */ + +.content-title { +margin: 5px 0px 15px; +font-size: 14px; +text-decoration: underline; +} + +.bottomindent { +margin-bottom: 5px; +} + +.content-title-blog { +margin: 5px 0px 2px; +margin-left: 8px; +font-size: 16px; +text-decoration: none; +} + +.content-title-no1 { +margin: 5px 0px 15px; +font-size: 14px; +text-decoration: none; +} + +.content-title-mod { +margin: 5px 10px 15px; +font-size: 14px; +text-decoration: underline; +} + +.content-title-no { +margin: 0px 0px 0px; +font-size: 12px; +text-decoration: none; +} + +.content-box .cont { +margin: 10px 10px 15px 10px; +font-size: 11px; +line-height: 15px; +} + +.content-box .cont-list { +margin: 2px 10px 5px 10px; +} + +.content-box .sub-hdr { +margin: 2px 10px 10px; +font-size: 12px; +text-decoration: underline; +} + +.content-box .sub-hdr-mod { +margin: 2px 10px 10px; +font-size: 11px; +text-decoration: none; +} + +/* Bottom footer box */ + +.footer-col { +margin: 0 0 0 4px; +} + +.footer-box { +border: 1px solid #000000; +background:#343234; +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +color:#ffffff; +padding: 0 0 0 15px; +} + +.footer-box .cont1 { +margin: 8px 10px 5px 5px; +font-size: 11px; +line-height: 15px; +} +.footer-box .cont2 { +margin: 2px 10px 15px 5px; +font-size: 11px; +line-height: 15px; +} + +.footer-box input { +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +} + +/* Copyright layer */ + +.copyright { +background: #c5c6ac; +font-family: verdana, arial, tahoma, sans-serif; +font-size: 10px; +font-weight: bold; +padding: 6px; +border: solid #000000; +border-width: 1px 0 0; +} + +.rh-ad { +padding: 10px 0; +} + +/* Terms of use on front page */ +.tou { +margin: 0 0 0 10px; +} + + +/* for templates info */ +.temp-lh { +float : left; +width : 100px; +margin : 0; +padding : 2px 10px 2px 0; +text-align : left; +} +.temp-rh { +margin : 0; +padding : 2px 0; +text-align : left; +} +.date { +font-size : 10px; +color : #787d63; +background : inherit; +} +.temp-list ul { +font-size : 11px; +margin : 0; +padding : 0; +list-style-type : none; +} +.temp-list { +padding : 0 0 0 15px; +} + +/* small*/ +small.fsize { +font-size : 9px; +color : #999999; +background : inherit; +} + +.code { +color : #663300; +background : #eeeeee; +font-family : verdana, arial, sans-serif; +font-size : 11px; +line-height : 20px; +padding : 4px 10px; +margin : 10px 20px; +border : 1px dashed #999999; +} + + +/* Search form on front page */ + +.search-form { +color: #000; +background: #c5c6ac; +padding: 5px; +margin: 0px 0 10px 30px; +border: 1px solid #333333; +width:465px; +text-align: center; +white-space: nowrap; +} + +/* Search forms on Search page */ + +.adv-search { +margin: 0 0 0 15px; +} + +/* Tool Tip */ +.tip { +cursor : help; +border-bottom : 1px dashed #555555; +} + +/* ADVERTISEMENTS */ +/* container div is 'id=adspace' */ +#adspace { +background: inherit; +} +.ads { +background: inherit; +margin: 5px 0 15px 4px; +font-size: 11px; +padding: 5px; +border: solid #555; +border-width: 1px 0 0 1px; +} +.ads a:link { font-size: 11px; } diff --git a/site/common/ads/ad_120x240.html b/site/common/ads/ad_120x240.html new file mode 100644 index 0000000..613e6f2 --- /dev/null +++ b/site/common/ads/ad_120x240.html @@ -0,0 +1,18 @@ + + + + + + + + + + diff --git a/site/common/ads/ad_120x600_noroadblocks.html b/site/common/ads/ad_120x600_noroadblocks.html new file mode 100644 index 0000000..20efc78 --- /dev/null +++ b/site/common/ads/ad_120x600_noroadblocks.html @@ -0,0 +1,21 @@ + + + + + + + + + + + + + diff --git a/site/common/ads/ad_120x600_roadblocks.html b/site/common/ads/ad_120x600_roadblocks.html new file mode 100644 index 0000000..f41e67e --- /dev/null +++ b/site/common/ads/ad_120x600_roadblocks.html @@ -0,0 +1,21 @@ + + + + + + + + + + + + + diff --git a/site/common/ads/ad_300x250_noroadblocks.html b/site/common/ads/ad_300x250_noroadblocks.html new file mode 100644 index 0000000..376336d --- /dev/null +++ b/site/common/ads/ad_300x250_noroadblocks.html @@ -0,0 +1,15 @@ + + +
+
+ +
+ \ No newline at end of file diff --git a/site/common/ads/ad_300x250_roadblocks.html b/site/common/ads/ad_300x250_roadblocks.html new file mode 100644 index 0000000..e04a5cb --- /dev/null +++ b/site/common/ads/ad_300x250_roadblocks.html @@ -0,0 +1,15 @@ + + +
+
+ +
+ \ No newline at end of file diff --git a/site/common/ads/ad_300x600.html b/site/common/ads/ad_300x600.html new file mode 100644 index 0000000..78abe32 --- /dev/null +++ b/site/common/ads/ad_300x600.html @@ -0,0 +1,14 @@ + + +
+ +
+ \ No newline at end of file diff --git a/site/common/ads/ad_728x90.html b/site/common/ads/ad_728x90.html new file mode 100644 index 0000000..81d9ce2 --- /dev/null +++ b/site/common/ads/ad_728x90.html @@ -0,0 +1,14 @@ + + +
+ +
+ \ No newline at end of file diff --git a/site/common/ads/ad_wallpaper.html b/site/common/ads/ad_wallpaper.html new file mode 100644 index 0000000..3b220d7 --- /dev/null +++ b/site/common/ads/ad_wallpaper.html @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/site/common/static/css/.style.css.swo b/site/common/static/css/.style.css.swo new file mode 100644 index 0000000..e0782ce Binary files /dev/null and b/site/common/static/css/.style.css.swo differ diff --git a/site/common/static/css/articles.css b/site/common/static/css/articles.css new file mode 100644 index 0000000..a0fd3c2 --- /dev/null +++ b/site/common/static/css/articles.css @@ -0,0 +1,39 @@ +@import url(core.css); + +.headbox .botbox { + border-right: 0; + height: 15px; +} + +.line { + margin: 20px 0; +} + +/* privacy */ +.headbox .published-date { + float: right; + color: #adadad; +} + +.headbox .author { + padding-left: 20px; +} + +.headbox .author, +.headbox .author a { + overflow: hidden; + text-transform: uppercase; + font-size: 15px; + color: #adadad; +} + +/* about us */ +.bio { + margin: 20px 0; +} + +.bio .bio-photo { + float: left; + padding-right: 10px; +} + diff --git a/site/common/static/css/core.css b/site/common/static/css/core.css new file mode 100644 index 0000000..74d1993 --- /dev/null +++ b/site/common/static/css/core.css @@ -0,0 +1,2698 @@ +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +nav, +section, +summary { + display: block; +} + +audio, +canvas, +video { + display: inline-block; +} + +audio:not([controls]) { + display: none; + height: 0; +} + +[hidden] { + display: none; +} + +/* global */ +html { + color: #000000; + font-family: 'Open Sans'; + font-size: 13px; + font-weight: normal; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +body { + margin: 0; + padding: 0; +} + +a:active, +a:hover { + outline: 0; +} + +a:hover { + text-decoration: underline; +} + +a { + text-decoration: none; + outline: 0; + color: #0b4fa0; +} + +p { + display: block; + -webkit-margin-before: 1em; + -webkit-margin-after: 1em; + -webkit-margin-start: 0px; + -webkit-margin-end: 0px; +} + +h1, h2, h3, h4 { + font-family: Oswald; + color: #000; + font-weight: normal; +} + +h1 { + font-size: 39px; + margin: 0; + padding: 4px 0 6px 0; +} + +h2 { + font-size: 27px; + font-weight: bold; +} + +h3 { + font-size: 22px; + margin: 0; + padding: 7px 0; +} + +h4 { + font-size: 18px; + text-transform: uppercase; + margin: 20px 0 0 0; + padding: 0px; +} + +p { + padding: 5px 0; + margin: 0; + text-align: justify; +} + +strong, b { + font-weight: bold !important; +} + +.clearall, .line { + clear: both; +} + +.clearfix:before, +.clearfix:after, +.row:before, +.row:after, +.row-b:before, +.row-b:after { + display: table; + content: " "; +} + +.clearfix:after, +.row:after, +.row-b:after { + clear: both; +} + +.center { + display: block; + margin: 0 auto; +} + +/* sprite-* */ +[class*="sprite-"] { + display: inline-block; + text-indent: -99999px; + text-align: left; + background-repeat: no-repeat; + background-image: url(images/sprite.svg); +} + +.sprite-logo { + float: left; + width: 95px; + height: 90px; + background-position: 0 -198px; +} + +.sprite-logo-sm { + float: left; + width: 50px; + height: 50px; + background-position: -100px -200px; +} + +.sprite-search-btn { + width: 33px; + height: 33px; + background-size: 320px 234px; + background-position: -15px -13px; + background-color: #0b4fa0; +} + +.expand { + display: none; + position: absolute; + width: 40px; + height: 100%; + top: 0; + right: 0; + cursor: pointer; +} + +.sprite-expand { + display: none; + position: absolute; + width: 14px; + height: 14px; + background-position: -100px -100px; + right: 15px; + top: 20px; +} + +.open .sprite-expand { + background-position: -120px -100px; +} + +.sprite-arrow-down { + width: 10px; + height: 10px; + background-position: -397px -197px; +} + +.sprite-next, +.sprite-nonext, +.sprite-prev, +.sprite-noprev { + width: 8px; + height: 11px; +} + +.sprite-next { + background-position: -588px -220px; + margin-right: 10px; +} + +.sprite-nonext { + background-position: -609px -220px; + margin-right: 10px; +} + +.sprite-prev { + background-position: -589px -200px; + margin-left: 10px; +} + +.sprite-noprev { + background-position: -610px -200px; + margin-left: 10px; +} + +.sprite-first, +.sprite-nofirst, +.sprite-last, +.sprite-nolast { + width: 15px; + height: 11px; +} + +.sprite-first { + background-position: -549px -200px; +} + +.sprite-nofirst { + background-position: -569px -200px; +} + +.sprite-last { + background-position: -549px -220px; +} + +.sprite-nolast { + background-position: -569px -220px; +} + +.sprite-arrow-down-b { + background-position: -410px -197px; + width: 10px; + height: 10px; +} + +.social a { + float: left; + display: inline-block; + width: 60px; + height: 56px; + margin-left: 2px; +} + +.social .sprite-twitter { + background-position: -384px -48px; + background-color: #62c8f8; +} + +.social .sprite-vimeo { + background-position: -434px -48px; + background-color: #009fd9; +} + +.social .sprite-facebook { + background-position: -479px -48px; + background-color: #0b4fa0; +} + +.social .sprite-tumblr { + background-position: -528px -48px; + background-color: #3a5976; +} + +.social .sprite-youtube { + background-position: -581px -50px; + background-color: #000; +} + +.social-sm a, +.social-sm span { + width: 30px; + height: 23px; +} + +.social-sm .sprite-magnify, +.sprite-magnify { + background-position: -100px -48px; + width: 25px; +} + +.social-sm .sprite-twitter { + background-position: -145px -48px; +} + +.social-sm .sprite-vimeo { + background-position: -196px -48px; +} + +.social-sm .sprite-facebook { + background-position: -242px -48px; + width: 25px; +} + +.social-sm .sprite-tumblr { + background-position: -294px -49px; + width: 25px; +} + +.social-sm .sprite-youtube { + background-position: -345px -49px; + margin-right: 6px; +} + +.bg-1 { + background-color: #0b4fa0; + color: #fff; +} + +.bg-2 { + background-color: #006ae6; +} + +.bg-3 { + background-color: #e4f0ff; +} + +.bg-4 { + background-color: #adadad; +} + +.bg-5 { + background-color: #4f4f4f; +} + +.bg-6 { + background-color: #272727; +} + +.col-1, .col-2, .col-3, .col-4, .col-5, .col-6, .col-7, .col-8, .col-9, .col-10, .col-11, .col-12 { + float: left; +} + +.col-12 { + width: 100%; +} +.col-11 { + width: 91.66666666666666%; +} +.col-10 { + width: 83.33333333333334%; +} +.col-9 { + width: 75%; +} +.col-8 { + width: 66.66666666666666%; +} +.col-7 { + width: 58.333333333333336%; +} +.col-6 { + width: 50%; +} +.col-5 { + width: 41.66666666666667%; +} +.col-4 { + width: 33.33333333333333%; +} +.col-3 { + width: 25%; +} +.col-2 { + width: 16.666666666666664%; +} +.col-1 { + width: 8.333333333333332%; +} + +.btn { + display: inline-block; + font-family: Oswald; + font-size: 14px; + letter-spacing: 1px; + text-transform: uppercase; + padding: 6px 13px; + background-color: #0b4fa0; + color: #fff; + border: 0; + cursor: pointer; +} + +.btn:hover { + background-color: #272727; + text-decoration: none; +} + +.btn.default { + border: 1px solid #0b4fa0; + background-color: #fff; + font-size: 20px; + padding: 3px 13px; + min-height: 30px; + color: #0b4fa0; +} + +.btn.default:hover { + background-color: #0b4fa0; + color: #fff; + text-decoration: none; +} + +.txt, +.file, +select { + font-family: "Open Sans"; + font-size: 15px; + background-color: #e4f0ff; + padding: 0 1%; + height: 34px; + color: #272727; + border: 0; + margin-right: 5px; + margin-bottom: 2px; +} + +input::-webkit-input-placeholder { + color: #0b4fa0; +} + +.file { + padding: 8px; + font-size: 13px; + height: auto; +} + +textarea.txt { + height: 80px; + padding-top: 10px; + padding-bottom: 10px; +} + +.txt.smatext { + width: 80px; +} + +.txt.shotext { + width: 250px; +} + +.txt.midtext { + min-width: 250px; + width: 70%; +} + +.txt.lngtext { + width: 98%; +} + +.topbox { + margin: 75px 0 0 0; + height: 7px; + border: 0; + border-top: 1px dotted #4f4f4f; + border-left: 1px dotted #4f4f4f; + border-right: 1px dotted #4f4f4f; +} + +.botbox { + margin: 0; + height: 7px; + border: 0; + border-bottom: 1px dotted #4f4f4f; + border-left: 1px dotted #4f4f4f; + border-right: 1px dotted #4f4f4f; +} + + +.hidden, .hide { + display: none; +} + +.error { + color: red; +} + +.message { + color: green; +} + +.ascending, .descending { + font-size: 8px; + padding-left: 3px; +} + +.ascending:before { + content: "\25b2"; +} + +.descending:before { + content: "\25bc"; +} + +.line { + margin: 0; + height: 4px; + border: 0; + background: #0b4fa0; + background: -webkit-linear-gradient(left, #006ae6, #0b4fa0); /* For Safari 5.1 to 6.0 */ + background: -o-linear-gradient(right, #006ae6, #0b4fa0); /* For Opera 11.1 to 12.0 */ + background: -moz-linear-gradient(right, #006ae6, #0b4fa0); /* For Firefox 3.6 to 15 */ + background: linear-gradient(to right, #006ae6, #0b4fa0); /* Standard syntax */ +} + +.mobile { + display: none; +} + +.paging { + position: relative; + display: block; + white-space: nowrap; + color: #000; + text-transform: uppercase; + font-size: 14px; + vertical-align: middle; + padding: 0; +} + +.toolbar-b h1 { + overflow: hidden; + display: inline-block; +} + +.toolbar-b .paging { + float: right; + margin-top: 15px; +} + +.paging span { + vertical-align: middle; +} + +.paging select { + color: #0b4fa0; + font-size: 12px; + height: auto; + padding: 2px; + margin: 3px 5px 0 5px; + vertical-align: middle; +} + +.paging a { + color: #0b4fa0; + text-decoration: none; +} + +.paging a:hover { + color: #006ae6; +} + +/* navbar */ +.mashead { + padding: 0; + border-bottom: 1px solid #adadad; +} + +.mashead .navbar .nav > li > a { + font-weight: bold; + font-size: 15px; + padding: 7px 8px; + margin: 0 5px; + display: inline-block; +} + +.mashead .navbar .nav > li > a:hover { + text-decoration: none; + background-color: #0b4fa0; + color: #ffffff; +} + +.mashead .navbar .nav > li > a span { + font-size: 12px; + font-weight: normal; +} + +.mashead .navbar .nav > li > a span.down { + font-size: 8px; +} + +.mashead .sprite-brand { + float: left; + width: 167px; + height: 14px; + background-position: -200px -202px; + margin-top: 12px; +} + +.mashead .navbar .nav { + float: right; +} + +.mashead .brand-b { + padding: 15px 0; + border-top: 1px solid #adadad; +} + +.mashead .brand-b .sprite-brand, +.mashead .brand-b .navbar.user-profile { + float: left; +} + +.mashead .controller { + position: absolute; + right: 10px; + top: 46px; +} + +.mashead .controller > a { + height: 40px; + width: 40px; +} + +.mashead .controller .sprite-magnify { + background-position: -50px -95px; + border-right: 2px solid #0b4fa0; + width: 46px; +} + +.mashead .controller .sprite-menu { + background-position: -442px -194px; +} + +.mashead .controller .sprite-menu.open { + background-image: none; + background-color: #0b4fa0; + background-size: 1051px 814px; + background-position: -768px -300px; + height: 45px; +} +.navbar .nav { + padding: 0; + margin: 0; +} + +.navbar .nav > li { + float: left; + list-style: none; +} + +.navbar.menu { + text-align: center; + height: 40px; + background-color: #0b4fa0; +} + +.navbar.menu .nav { + position: relative; + margin: 0; + padding: 0; +} + +.container .header .navbar.menu .nav { + width: 100%; + background-color: #0b4fa0; +} + +.container .header .navbar.menu .nav > li { + background-color: #0b4fa0; +} + +.container .header .navbar.menu .nav > li:last-child a { + padding-right: 16px; + padding-left: 15px; +} + +.navbar.menu .nav, +.navbar.utility > .nav, +.navbar.user-profiley > .nav { + display: inline-block; + list-style: inside none; +} + +.navbar.menu .nav > li, +.navbar.utility > .nav > li, +.navbar.user-profile > .nav > li { + list-style: inside none; + display: inline-block; +} + +.navbar.utility > .nav > li > a { + display: inline-block; + font-weight: normal; + margin: 0; +} + +.navbar.utility > .nav > li:hover > a, +.navbar.user-profile > .nav > li:hover > a.logged-in, +.navbar.user-profile > .nav > li:hover > a { + background-color: #0b4fa0; + font-weight: normal; + color: #fff; +} + +.navbar.utility > .nav > li:hover .sprite-arrow-down, +.navbar.user-profile > .nav > li:hover .sprite-arrow-down { + background-position: -408px -197px +} + +.navbar.menu .nav > li { + float: left; +} + +.navbar.menu .nav > li > a, +.navbar.menu-b .nav > li > a { + color: #ffffff; + padding: 7px 17px; + font-family: Oswald; + font-size: 17px; + text-transform: uppercase; + display: inline-block; + letter-spacing: 1.20px; +} + +.container .navbar.menu > ul > li > a, +.container-b .navbar.menu > ul > li > a { + padding: 7px 13px; +} + +.navbar.user-profile .nav > li > a { + color: #0b4fa0; + padding: 1px 10px; + margin: 0; + text-transform: uppercase; + font-family: Oswald; + font-size: 14px; + letter-spacing: 1.20px; + display: block; +} + +.navbar.user-profile .nav > li > a.logged-in { + color: #0b4fa0; + padding: 2px 10px; + margin: 0; + text-transform: none; + font-family: "Open Sans"; + font-size: 14px; + font-weight: normal; + letter-spacing: 0; +} + +.container .footer .navbar.menu > ul > li > a { + padding: 0px 10px; +} + + +.navbar.menu .nav > li.selected > a { + background-color: #272727; +} + +.navbar.menu .nav > li > a:hover, +.navbar.menu .nav > li:hover > a { + background-color: #272727; + text-decoration: none; +} + +.navbar.menu-b { + position: absolute; + display: none; + top: 45px; + right: -15px; + width: 250px; + z-index: 999; +} + +.navbar.menu-b .nav { + width: 100%; + display: block; +} + +.navbar.menu-b .nav > li { + float: none; + display: block; + background-color: #0b4fa0; + padding: 0; +} + +.navbar.menu-b .nav > li > a { + display: block; + padding: 7px 0px; + line-height: 2em; + margin-left: 25px; + border-bottom: 1px solid #268aff; +} + +.navbar.menu-b .nav > li:last-child > a { + border: 0; +} + +.navbar.menu-b .nav > li > a:hover { + background-color: inherit; + color: #adadad; +} + +/* form */ +.row, .row-b { + padding: 5px 0; +} + +.row-b { + background-color: #e4f0ff; +} + +.form .row, +.form .row-b { + padding: 5px 0; +} + +.row .value { + overflow: hidden; +} + +.row .name { + float: left; + display: inline-block; + margin-right: 10px; + font-weight: bold; + font-size: 14px; + margin-top: 7px; + vertical-align: middle; +} + +/* subscribe */ +.navbar.utility .subscribe { + position: relative; +} + +.navbar.utility .subscribe.open a { + background-color: #0b4fa0; + color: #fff; +} + +.navbar.utility .subscribe-form { + position: absolute; + top: 34px; + right: 0; + width: 310px; + padding: 15px; + z-index: 1000; + background-color: #fff; + border: 1px solid #adadad; + box-shadow: 1px 1px 1px #a4a4a4; + display: none; + text-align: center; +} + +.navbar.utility .subscribe-form .btn, +.navbar.utility .subscribe-form .txt { + float: left; + display: inline-block; +} + +/* sub navbar */ +.subnav { + visibility: hidden; + -webkit-box-sizing: content-box; + box-sizing: content-box; + position: absolute; + background-color: #272727; + top: 0px; + left: 0px; + border-radius: 0 0 5px 5px; + z-index: 999; + text-align: left; + opacity: 0; +} + +.subnav hr { + display: none; + width: 45px; + height: 1px; + margin: 3px 0; + padding: 0; + border: 0; + border-bottom: 2px dotted #006ae6; +} + +.subnav ul { + float: left; + margin: 0; + padding: 0; + height: 100%; + border-right: 1px dotted #0b4fa0; +} + +.subnav ul:first-child { + margin-left: 30px; +} + +.subnav ul:last-child { + border: 0; +} + +.navbar .subnav li { + float: none; + list-style: none; + padding: 0; + margin: 0; +} + +.subnav a:hover { + color: #006ae6; +} + +.subnav a { + min-width: 110px; + font-family: "Roboto Slab"; + font-weight: normal; + font-size: 16px; + color: #fff; + letter-spacing: 0; + display: block; + padding: 5px 15px; + text-transform: capitalize; +} + +.subnav a:hover { + text-decoration: none; + color: #006ae6; +} + +.navbar.utility > .nav > li > .subnav { + top: 34px; + width: 100%; + margin: 0; + padding: 0; +} + +.navbar.user-profile .subnav { + left: 0px; + top: 23px; + width: 180px; +} + +.navbar.utility .subnav ul { + margin: 0; + padding: 0; + width: 100%; +} + +.navbar.utility .nav > li { + position: relative; + padding: 0; +} + +.navbar.utility > .nav > li > .subnav a, +.navbar.user-profile > .nav > li > .subnav a { + display: block; + padding: 8px 12px; + font-size: 13px; + margin: 0; +} + +.navbar.utility .nav > li > .subnav a.logout { + border-top: 1px dotted #0b4fa0; + padding: 15px 0px 15px 12px; +} + +.navbar.menu .subnav { + top: 38px; + height: 155px; + width: 100%; + padding: 25px 0; +} + +.navbar.menu .subnav ul:first-child a { + padding-left: 0; +} + +.navbar.menu .nav > li:hover > .subnav, +.navbar.utility > .nav > li:hover > .subnav, +.navbar.user-profile > .nav > li:hover > .subnav { + display: block; + visibility: visible; + opacity: 1; + transition: visibility 1s linear 0.3s, opacity 0.3s linear; +} + +.navbar.menu-b .subnav { + position: relative; + padding: 5px 0; + top: auto; + left: auto; +} + +.navbar.menu-b .sprite-expand, +.navbar.menu-b .expand { + display: block; +} + +.navbar.menu-b .nav > li > .subnav a { + font-size: 14px; + padding: 5px 25px 5px 0px; +} + +.navbar.menu-b .nav > li.open > a { + border: 0; +} + +.navbar.menu-b .nav > li > .subnav hr { + display: inline-block; +} + +.navbar.menu-b .nav > li > .subnav ul { + float: none; + width: auto; + margin: 0 0 0 35px; + padding: 0; + border-right: 0; +} + +.navbar.menu-b .nav > li > .subnav li { + padding: 0; + margin: 0; + background-color: #272727; + text-align: left; +} + +/* main container */ +.container { + position: relative; + width: 995px; + padding: 0; + margin: 0 auto; + background-color: #fff; + box-shadow: 0 0 35px 5px rgba(50,50,50,0.5); + /*margin-left: 448px;*/ +} + +.container-b { + width: 100%; + padding: 0; + margin: 0; +} + +.wallpaper { + display: block; + position: fixed; + top: 0; + left: 0; + width: 100%; + max-width: 1920px; + max-height: 1050px; + overflow: hidden; + text-align: center; +} + +.wallpaper img { + display: block; + position: relative; + height: auto; + width: 1920px;; + left: 50%; + margin-left: -960px; + cursor: pointer; +} + +/* header & navbar */ +.header { + position: relative; + margin-left: 20px; +} + +.header .brand-b { + display: none; +} + +.header .mashead, +.header .mashead-b { + margin-right: 85px; +} + +.header .mashead-b { + position: relative; + text-align: center; + padding: 15px 0; + height: 124px; +} + +.header .mashead-b .social-sm { + position: absolute; + bottom: 10px; + right: 0; +} + +.header .mashead-b .adbox { + overflow: hidden; + text-align: center; +} + +.header .utils { + position: absolute; + right: 0px; + top: 0px; + padding: 15px 0; +} + +.header .utils a { + display: block; + width: 59px; + height: 43px; + margin-bottom: 14px; + cursor: pointer; +} + +.header .utils .sprite-swim { + background-position: -0px -300px; +} + +.header .utils .sprite-bike { + background-position: 0 -350px; +} + +.header .utils .sprite-run { + background-position: 0 -400px; +} + +/* main body */ +.main { + margin: 4px 0 4px 25px; +} + +.container-b .main { + margin-right: 25px; +} + +.contentwrapper { + height: auto; + overflow: hidden; +} + +.content { + overflow: hidden; + padding-right: 20px; +} + +.container-b .content { + padding-right: 25px; +} + +.sidebar { + float: right; + width: 300px; + margin-right: 25px; + padding-top: 19px; +} + +.container-b .sidebar { + margin-right: 0; +} + +/* breadcrumb */ +.breadcrumb { + font-size: 12px; + color: #adadad; +} + +.breadcrumb a, +.breadcrumb span { + color: #adadad; +} + +/* footer */ +.footer { + margin: 10px 0 0px 25px; +} + +.footer p { + padding: 0; + margin: 0; +} + +.footer .masfoot { + height: 56px; + position: relative; + border-right: 250px solid #fff; + background-color: #0b4fa0; + background: -webkit-linear-gradient(left, #006ae6, #0b4fa0); /* For Safari 5.1 to 6.0 */ + background: -o-linear-gradient(right, #006ae6, #0b4fa0); /* For Opera 11.1 to 12.0 */ + background: -moz-linear-gradient(right, #006ae6, #0b4fa0); /* For Firefox 3.6 to 15 */ + background: linear-gradient(to right, #006ae6, #0b4fa0); /* Standard syntax */ +} + +.footer .navbar.menu { + height: 38px; + background-color: inherit; +} + +.footer .masfoot-b { + padding: 30px 20px; +} + +.footer .navbar { + padding-top: 18px; +} + +.footer .navbar.menu ul > li > a { + color: #ffffff; + padding: 0px 15px; + font-family: "Open Sans"; + font-size: 15px; + text-transform: uppercase; + display: inline-block; + border-right: 1px dotted #e4f0ff; +} + +.footer .navbar.menu ul > li > a.agreement { + border: 0; +} + +.footer .navbar.menu ul > li.selected > a { + background-color: none; +} + +.footer .navbar.menu ul > li > a:hover { + background-color: inherit; + text-decoration: underline; +} + +.footer .social { + position: absolute; + top: 0; + right: -250px; +} + +.footer .sprite-logo { + width: 95px; + height: 90px; + background-position: 0 -198px; +} + +.footer .newsletter { + float: left; + width: 370px; +} + +.footer .newsletter h2 { + text-transform: uppercase; + letter-spacing: 2px; + margin: 0 0 10px 0; + font-size: 24px; +} + +.sponsors nav { + padding: 20px 20px 30px 0; + color: #666; + text-align: justify; +} + +.sponsors a { + color: #000; + font-size: 15.5px; +} + +.sponsors .line { + margin-bottom: 0; +} + +/* +.footer .newsletter .btn { + float: left; + letter-spacing: 1.5px; + margin-top: 0px; + font-size: 17px; + height: 32px; + padding-top: 3px; +} + +.footer .newsletter .txt { + float: left; + margin: 0 8px 0 0; + max-width: 270px; + width: 60%; +} +*/ + +.footer .contact-us { + color: #006ae6; + font-size: 15px; + overflow: hidden; +} + +.footer .contact-us .addr { + float: right; + display: inline-block; + text-align: right; + margin-right: 20px; + margin-top: 15px; + font-size: inherit; + font-weight: normal; + color: #0b4fa0; +} + + + +.footer .contact-us .sprite-logo { + float: right; +} + +.footer .adv-withus { + max-width: 300px; + width: 80%; + padding: 20px 0 20px 20px; + border-right: 3px solid #fff; +} + +.footer .adv-withus a { + float: left; + color: #fff; + text-transform: uppercase; + letter-spacing: 1.5px; + font-size: 17px; + margin-top: 13px; + margin-left: 18px; + font-family: Oswald; +} + +.footer .copyright { + float: right; + display: inline-block; + height: 53px; + padding: 13px 30px; + font-size: 13px; + text-align: right; +} + +.container .footer .masfoot-b { + padding: 20px; +} + +.container .header .mashead-b .adhori { + float: right; +} + +/* dtable */ +.dtable { + display: table; + width: 100%; + color: #0b4fa0; + color: #000; + font-size: 15px; +} + +.dtable a { + color: #0b4fa0; +} + +.drow { + display: table-row; + font-size: 13px; +} + +.dcell { + display: table-cell; + padding: 6px; + font-size: 13px; +} + +.dhead { + display: table-header-group; +} + +.dhead .drow { + background-color: #4f4f4f; + color: #fff; + letter-spacing: 0.5px; +} + +.dhead a { + color: inherit; + display: inline-block; + text-decoration: none; + width: 100%; +} + +.dhead a:hover { + color: #adadad; +} + +.dbody { + display: table-row-group; +} + +.dbody .dcell { + border-bottom: 1px dotted #adadad; +} + +.dtable.bg .dbody .dcell { + background-color: #f4f8fb; +} +.adwrapper { + text-align: center; +} + +/* dtable form */ +.dtable.form { + margin: 15px 0; +} + +.dtable.form .dcell { + padding: 4px 17px; + font-size: 13px; +} + +.dtable.form .name { + width: 20%; + text-align: right; + padding-left: 0px; + vertical-align: middle; +} +.dtable.form .value { + padding: 4px 0; +} + +.dtable.form > .drow:first-child .dcell { + padding-top: 20px; +} + +.dtable.form .divider { + width: 1px; + border-left: 1px dotted #adadad; + padding: 4px 17px 4px 0; +} + +.dtable.form .drow:first-child .divider { + border-top: 1px dotted #adadad; +} + +.dtable.form .actions .divider { + border: 0; +} + +.dtable.form .actions .dcell:last-child { + padding-top: 20px; + text-align: left; +} + + +/* floating box */ +.social-sm a { + float: left; + margin-top: 5px; +} + +.main-search { + float: left; + border-left: 1px solid #adadad; + height: 31px; + max-width: 350px; + min-width: 13px; + padding-left: 15px; +} + +.main-search form { + display: none; +} + +.main-search .txt { + float: left; + display: block; + width: 180px; + padding: 5px; + height: auto; + font-size: 14px; + margin-top: 1px; + height: auto; +} + +.main-search select { + float: left; + font-size: 14px; + height: 30px; + width: 140px; +} + +.search-form { + display: none; + padding: 0 10px; + margin-bottom: 10px; +} + +.search-form .row, +.search-form .row-b { + position: relative; +} + +.search-form .sprite-search-btn { + position: absolute; + top: 10px; + left: 12px; + width: 25px; + height: 25px; + background-size: 338px 262px; + background-position: -19px -20px; + background-color: #e4f0ff; +} + +.search-form .txt { + float: left; + border: 0; + padding: 5px 0 0 40px; + font-size: 15px; + color: #0b4fa0; + width: 87%; + font-weight: bold; +} + +.search-form .sprite-x { + float: right; + margin-top: 15px; + margin-right: 15px; + background-position: -500px -200px; + width: 13px; + height: 13px; +} + +.search-form select { + border: 2px solid #e4f0ff; + background-color: #fff; + height: 40px; + margin-right: 10px; +} + +.search-form .btn { + float: right; + height: 39px; + font-size: 17px; + margin: 0; +} + +/* ad */ +.adbox { + text-align: center; + margin-left: 25px; +} + +.adhori { + display: block; + width: 728px; + height: 90px; + margin: 0 auto; +} + +/* sidebar widget */ +.dynamic-widget { + display: none; +} +.widget { + padding: 0 15px; +} + +.widget.nodivider { + margin-bottom: 40px; +} + +.widget-spacer { + display: block; + height: 25px; +} + +.widget .label { + margin-bottom: 15px; +} + +.widget-form .btn { + min-width: 80px; +} + +.widget-fullwidth { + padding: 0; +} + +.widget img { + max-width: 300px; +} + +.widget .excerpt { + margin: 10px 0; +} + +.widget .product-image { + margin: 20px auto 10px auto; +} + +.widget .product { + font-size: 13px; +} + +.widget .product .excerpt { + display: block; + margin: 15px 0; +} + +.widget h2 { + text-align: center; + font-size: 24px; + text-transform: uppercase; + letter-spacing: 2px; + margin: 0 0 5px 0; +} + +.widget h3 { + text-align: center; + font-size: 22px; + color: #0b4fa0; + padding-top: 0; + padding-bottom: 5px; + margin-bottom: 5px; + border-bottom: 1px solid #adadad; + font-weight: 500; +} + +.widget.poll .question { + font-size: 13px; + padding: 0 0 15px 0; +} + +.widget .description { + font-size: 13px; + text-align: center; + padding: 0 0 15px 0; +} +.content-divider-top, +.widget-divider { + display: block; + width: 100%; + height: 20px; + border-left: 1px dotted #4f4f4f; + border-bottom: 1px dotted #4f4f4f; +} +.content-divider-top { + margin-bottom: 15px; +} +.widget-divider { + margin-bottom: 20px; +} +.content-divider-bottom { + display: block; + width: 100%; + height: 20px; + border-left: 1px dotted #4f4f4f; + border-top: 1px dotted #4f4f4f; + margin-top: 40px; + margin-bottom: 0; +} +.widget-form .row .btn { + width: 95px; + float: right; + margin-left: -95px; + position: relative; + z-index: 100; +} +.widget-form .row .txt { + overflow: hidden; + width: auto; + float: none; + height: 31px; + width: 145px; +} + +.widget.has-border { + padding-right: 0; +} + + +.widget .list .link { + display: block; + width: 100%; + padding: 7px 3px 7px 0; +} +.widget .list .link a { + display: table-cell; + padding-left: 10px; + color: #272727; +} +.widget .list .link p { + color: #adadad; + margin-left: 40px; + padding: 0; +} + +.widget .list .link div { + display: table-cell; + position: relative; + top: 3px; +} +.widget .list span { + display: block; + text-align: center; + font-size: 12px; + width: 29px; + background: #0b4fa0; + color: #fff; + font-weight: 500; + font-family: 'Oswald', sans-serif; +} +.widget .list.dated span { + width: 46px; + text-transform: uppercase; + font-weight: 400; +} + + +.widget .listings { + font-size: 13px; + text-transform: uppercase; + font-family: Oswald; + width: 100%; + display: block; +} + +.widget .listings > div { + display: inline-block; + text-align: center; + position: relative; + max-width: 24%; +} + +.widget .listings a > div { + height: 52px; + line-height: 52px; + text-align: center; +} + +.widget .listings [class*="sprite-"] { + display: block; + width: 52px; + height: 52px; + margin: 10px auto 3px auto; +} + +.icon-divider { + width: 0; + height: 30px; + border-right: 1px dotted #332F2F; + position: relative; + top: 20px; + margin-right: -1px; +} +.icon-spacer { + width: 1px; + height: 30px; + position: relative; + top: 20px; + margin-right: -1px; +} + +.widget.collapse { + position: relative; + display: block; + padding: 0; +} + +.widget.collapse .lnk { + display: block; + background: #0b4fa0; + padding: 5px 10px; + color: #fff; + font-family: Oswald; + font-size: 13px; + text-transform: uppercase; + letter-spacing: 1.20px; + cursor: pointer; + text-align: left; +} +.widget.collapse .lnk:hover { + text-decoration: none; +} +.widget.collapse .sprite-arrow-down-b { + float: right; + margin-top: 5px; + margin-left: 5px; +} + +.widget.collapse .tagwidget { + display: none; + position: absolute; + width: 276px; + background: #fff; + top: 30px; + left: 0; + z-index: 10; + border: 2px solid #ccc; + padding: 10px; + box-shadow: 0px 0px 5px 0px #a4a4a4; +} + +.widget.collapse .center { + margin-top: 15px; + text-align: center; +} + + +/* POLL OERRIDE */ +.poll { + margin-top: 25px; +} +.poll .dtable { + margin-bottom: 15px; +} +.poll .dtable .dcell { + padding: 2px; +} + +.poll .result-votes { + display: block; + text-align: center; + width: 100%; + margin: 5px 0 15px 0; + font-weight: bold; +} + +.poll .percentage { + width: 35px; + color: #0b4fa0; + font-weight: bold; +} + +.poll .meter { + width: 80px; +} + +.poll .meter div { + display: block; + background-color: #e4f0ff; + width: 100%; + height: 13px; + vertical-align: middle; +} + +.poll .meter span { + display: block; + height: 100%; + border: 0px solid #0b4fa0; +} + +.poll .input-group.choice { + display: block; + padding: 0 0 5px 5px; +} + +.poll .subtitle { + color: #0b4fa0; + font-size: 18px; + font-family: 'Oswald', sans-serif; + padding-bottom: 5px; + margin-bottom: 5px; + border-bottom: 1px solid #a0a0a0; + font-weight: 400; + display: block; + text-align: center; +} + +.poll .desc { + margin: 10px 0; +} + +.poll .answers { + margin: 10px 0; +} + +.poll .option { + line-height: 1.4; + padding: 5px 0; + color: #0b4fa0; +} + +.poll .option input { + float: left; + margin-right: 5px; +} + +.poll .option label { + overflow: hidden; +} + +.poll .lnk { + display: block; + margin-top: 10px; + text-align: center; + font-weight: bold; +} + +.poll .btn-wrapper { + float: none; + text-align: center; + padding: 0; +} + +.poll .btn-wrapper .btn { + float: none; +} + +@media (max-width: 1024px) { +/* decrease icons by 25% */ + .social a, + .social-sm a, + .header .utils a, + .footer .sprite-logo { + background-size: 540px 418px; + } + + .sponsors { + margin-left: 10px; + margin-right: 10px; + } + + .sponsors nav { + padding-right: 0; + padding-top: 0; + } + + .sponsors .section-divider { + display: none; + } + + .section-divider.mobile.tablet { + margin-bottom: 10px; + } + + .social a { + float: left; + display: inline-block; + width: 43px; + height: 43px; + margin-left: 1px; + } + + .navbar.menu { + height: 40px; + } + + .header { + margin-left: 12px; + } + .footer .navbar.menu { + height: 30px; + } + + .navbar.menu .nav > li > a { + padding: 7px 12px; + letter-spacing: 1px; + } + + .footer .sprite-logo { + width: 76px; + height: 70px; + background-position: 0 -156px; + } + + .footer .newsletter { + width: 280px; + } + + .footer .newsletter .btn { + letter-spacing: 1px; + margin-top: 0px; + font-size: 15px; + } + + .footer .newsletter .txt { + width: 50%; + } + + .footer .contact-us .addr { + margin-top: 5px; + font-size: 14px; + } + + .footer .social { + right: -221px; + } + + .footer .masfoot { + border-right: 221px solid #fff; + height: 43px; + } + + .footer .navbar { + padding-top: 13px; + } + + .footer .navbar.menu .nav > li > a { + font-size: 14px; + padding: 0px 10px; + } + + .footer .newsletter h2.forum { + font-size: 22px; + } + + .social .sprite-twitter { + background-position: -293px -46px; + background-color: #62c8f8; + } + + .social .sprite-vimeo { + background-position: -330px -46px; + background-color: #009fd9; + } + + .social .sprite-facebook { + background-position: -364px -46px; + background-color: #0b4fa0; + } + + .social .sprite-tumblr { + background-position: -423px -39px; + background-color: #3a5976; + } + + .social .sprite-youtube { + background-position: -443px -46px; + background-color: #000; + } + + .social-sm a { + display: inline-block; + width: 24px; + height: 18px; + } + + .social-sm .sprite-magnify { + width: 20px; + background-position: -80px -40px; + } + + .social-sm .sprite-twitter { + background-position: -120px -40px; + } + + .social-sm .sprite-vimeo { + background-position: -160px -40px; + } + + .social-sm .sprite-facebook { + background-position: -197px -40px; + } + + .social-sm .sprite-tumblr { + background-position: -240px -40px; + } + + .social-sm .sprite-youtube { + background-position: -277px -40px; + } + + .header .utils a { + width: 47px; + height: 34px; + margin-bottom: 10px; + } + + .header .utils .sprite-swim { + background-position: 0 -240px; + } + + .header .utils .sprite-bike { + background-position: 0 -280px; + } + + .header .utils .sprite-run { + background-position: 0 -320px; + margin-bottom: 0; + } + + .container .header .mashead-b .adhori { + float: none; + } +} + +@media (min-width: 1024px) { + .search-form { + display: none !Important; + } + +} + +/* tablet portrait and landscape */ +@media (max-width: 1023px) { + .subnav { + display: none; + visibility: visible; + opacity: 1; + } + + .container { + width: auto; + } + + .header .search-form { + margin-top: 10px; + padding-top: 10px; + border-top: 1px dotted #4f4f4f; + } + + + .header .mashead-b .social-sm, + .header .mashead-b .sprite-logo, + .header .navbar.menu, + .sidebar { + display: none; + } + + .header { + margin: 0; + padding: 0; + } + + .header .grphead { + position: relative; + margin-right: 72px; + margin-left: 25px; + } + + .header .utils { + top: 0; + right: 0; + padding: 0; + width: 47px; + } + + .header .utils a { + margin: 7px 0 0 0; + } + + .header .mashead { + border: 0; + padding-bottom: 0px; + margin: 0 0 14px 0; + box-shadow: 0px 3px 3px #a4a4a4; + } + + .header .brand .sprite-brand { + position: absolute; + top: 0; + left: 0; + width: 95px; + height: 90px; + background-position: 0 -198px; + } + + .header .grphead > .navbar { + margin-left: 120px; + min-height: 123px; + } + + .header .brand-b { + display: block; + } + + .header .mashead-b { + height: auto; + padding: 0; + margin: 0; + } + + .header .brand-b .sprite-brand { + width: 198px; + height: 18px; + background-position: -200px -200px; + } + + .footer .newsletter .txt { + width: 100px; + } + + .footer .newsletter .btn { + margin-top: 0px; + font-size: 17px; + } + + .footer .newsletter h2.forum { + font-size: + } + + .content { + padding-right: 0; + } + + .container-b .sidebar { + display: none; + } + + .adbox { + margin: 0 12px; + } + + .navbar.menu-b .nav > li:hover > a, + .navbar.menu-b .nav > li:hover > a:active { + color: #fff; + background-color: inherit; + } +} + +@media (max-width: 1023px) { + .main, + .container-b .main { + margin: 0 15px 14px 15px; + } + + .footer { + margin-left: 0; + } + + .footer .masfoot-b { + padding: 20px; + } + + .footer .navbar.menu .nav > li > a { + font-size: 12px; + padding: 0px 10px; + } + + .footer .contact-us .addr { + margin-right: 10px; + margin-top: 8px; + font-size: 13px; + } + + .content, + .container-b .content { + padding: 0; + } + + .adbox { + margin: 0 12px; + } + + .adbox .adhori { + float: none; + } + + .adbox .topbox, + .adbox .botbox { + display: none; + } +} + +@media (max-width: 767px) { + .main, + .container-b .main { + margin: 0; + } + + .content, + .container-b .content, + .adbox, + .footer .copyright, + .footer .newsletter { + margin-left: 10px; + margin-right: 10px; + } + + .breadcrumb { + margin: 0 10px; + } + + .adbox { + text-align: center; + margin: 0px 12px; + } + + .footer { + margin-left: 0; + margin-bottom: 10px; + } + + .footer p { + padding: 0; + margin: 0; + } + + .footer .masfoot { + width: 100%; + height: auto; + background: none; + margin: 0; + padding: 0; + border: 0; + } + + .footer .navbar.menu { + height: 32px; + padding-bottom: 0; + background-color: #0b4fa0; + background: -webkit-linear-gradient(left, #006ae6, #0b4fa0); /* For Safari 5.1 to 6.0 */ + background: -o-linear-gradient(right, #006ae6, #0b4fa0); /* For Opera 11.1 to 12.0 */ + background: -moz-linear-gradient(right, #006ae6, #0b4fa0); /* For Firefox 3.6 to 15 */ + background: linear-gradient(to right, #006ae6, #0b4fa0); /* Standard syntax */ + margin-bottom: 30px; + } + + .footer .navbar.menu .nav > li > a { + margin: 0; + font-size: 10px; + font-weight: normal; + } + + .footer .social { + position: relative; + display: block; + right: auto; + padding: 0; + text-align: center; + margin-bottom: 10px; + } + + .footer .social a { + display: inline-block; + float: none; + margin-left: 10px; + } + + .footer .social a:first-child { + margin-left: 0; + } + + .footer .masfoot-b { + padding: 0; + } + + .footer .newsletter { + float: none; + width: auto; + text-align: center; + padding: 0; + margin: 10px 25px; + } + + .footer .newsletter .botbox { + margin: 0px 0 0 0; + height: 15px; + border-right: 0; + } + + .footer h2.forum { + font-size: 27px; + margin: 0; + padding: 0; + } + + .footer .newsletter .txt, + .footer .newsletter .btn { + float: none; + display: inline-block; + vertical-align: middle; + } + + .footer .newsletter .txt { + width: 50%; + } + + .footer .newsletter .btn { + font-size: 17px; + } + + .footer .newsletter .row { + text-align: center; + } + + .footer .contact-us { + text-align: center; + margin: 5px 0; + } + + .footer .contact-us .addr { + float: none; + text-align: center; + font-size: 12px; + letter-spacing: .4px; + } + + .footer .sprite-logo { + display: none; + } + + .footer .bg-1 { + background-color: #fff; + } + + .footer .col-5, + .footer .col-7 { + float: none; + width: auto; + display: block; + } + + .footer .adv-withus { + position: relative; + background-color: #0b4fa0; + color: #fff; + border: 0; + max-width: none; + width: auto; + margin: 0 auto 15px auto; + padding: 0; + vertical-align: middle; + } + + .footer .adv-withus .sprite-logo-sm { + position: absolute; + left: 22%; + top: 8px; + } + + .footer .adv-withus a { + float: none; + overflow: hidden; + display: block; + width: auto; + text-align: center; + letter-spacing: 1.5px; + font-size: 17px; + margin: 18px 0 18px 0; + } + + .footer .copyright { + float: none; + display: block; + height: auto; + font-size: 10px; + margin: 0 25px; + padding: 0 0 20px 0; + text-align: center; + color: #0b4fa0; + font-weight: bold; + letter-spacing: .3px; + } + + .adbox .botbox, + .adbox .topbox { + display: none; + } + + .adbox .adhori { + float: none; + width: auto; + } + + .paging { + position: relative; + display: block; + width: 100%; + padding: 5px 0px 10px 0; + background-color: #e4f0ff; + text-align: center; + font-size: 20px; + font-family: Oswald; + margin-bottom: 15px; + } + + .toolbar-b .paging { + margin-top: 0; + } + + .toolbar .line { + display: none; + } + + .paging select { + font-size: 13px; + padding: 3px 1px; + text-align: center; + text-align-last: center; + } + + .paging a { + display: inline; + } + + .paging select, + .paging span { + display: inline-block; + vertical-align: middle; + } + + .paging .next, + .paging .last, + .paging .prev, + .paging .first { + position: absolute; + top: 5px; + } + + .paging .prev { + left: 30px; + } + + .paging .first { + left: 10px; + } + + .paging .next { + right: 30px; + } + + .paging .last { + right: 10px; + } + +} + +@media (max-width: 695px) { + .header .mashead-b, + .quick-launchers, + .dtable .author, + .dtable .views, + .dtable .replies, + .dtable .threads, + .dtable .posts, + .dtable .date, + .header .brand-b .sprite-brand, + .btn-wrapper .btn-new-post { + display: none; + } + + .btn.default { + padding: 3px 12px; + margin-top: 4px; + } + + .header .grphead { + position: relative; + margin: 10px 0 5px 25px; + } + + .header .mashead { + border: 0; + padding: 0; + margin: 0; + box-shadow: none; + /*height: 99px; */ + } + + .header .mashead .utils { + position: absolute; + width: auto; + top: 0; + right: 0; + } + + .header .mashead .utils a { + float: left; + display: inline-block; + margin: 4px 0 4px 5px; + } + + .navbar.utility { + padding: 0; + height: auto; + } + + .header .brand .sprite-brand { + position: relative; + float: left; + width: 70px; + height: 65px; + background-size: 506px 392px; + background-position: 0 -150px; + } + + .header .mashead .navbar.utility { + display: block; + float: right; + width: 75%; + margin: 0; + min-height: 0; + } + + .header .mashead .brand-b { + margin-top: 35px; + margin-bottom: 0; + } + + .header .brand-b .welcome { + display: inline; + } + + .header .mashead .welcome { + font-weight: bold; + text-transform: uppercase; + font-size: 15px; + padding-top: 5px; + } + + .header .search-form { + border: 0; + margin-top: 0px; + padding: 0; + } + + .header .search-form .row { + padding-left: 10px; + padding-right: 10px; + } + + .search-form .txt { + width: 78%; + } + + .header .mashead .navbar.utility > .nav { + display: none; + } + + .content, + .adbox, + .footer .copyright, + .footer .newsletter { + margin: 0 10px; + } + + .breadcrumb { + display: block; + padding: 5px 10px; + font-size: 15px; + background-color: #0b4fa0; + color: #fff; + background: -webkit-linear-gradient(left, #006ae6, #0b4fa0); /* For Safari 5.1 to 6.0 */ + background: -o-linear-gradient(right, #006ae6, #0b4fa0); /* For Opera 11.1 to 12.0 */ + background: -moz-linear-gradient(right, #006ae6, #0b4fa0); /* For Firefox 3.6 to 15 */ + background: linear-gradient(to right, #006ae6, #0b4fa0); /* Standard syntax */ + font-weight: bold; + white-space: nowrap; + overflow: hidden; + vertical-align: middle; + text-overflow: ellipsis; + max-width: 100%; + margin: 0; + } + + .breadcrumb a, + .breadcrumb span { + color: #fff; + font-weight: normal; + } + + .dcell { + padding-left: 1px; + padding-right: 1px; + } + + /* footer */ + .footer .adv-withus .sprite-logo-sm { + left: 10%; + } + + .footer .navbar.menu .nav > li > a { + letter-spacing: normal; + margin: 0; + } + + .footer .navbar.menu .nav > li > a.policy, + .footer .navbar.menu .aboutus { + text-indent: -9999px; + line-height: 0; + } + + .footer .navbar.menu .nav > li > a.policy:after { + content: 'Privacy'; + text-indent: 0; + line-height: initial; + display: block; + } + + .footer .navbar.menu .aboutus:after { + content: 'About'; + text-indent: 0; + line-height: initial; + display: block; + } + + .mobile { + display: block; + } + + .navbar.menu-b .nav > li:hover > a, + .navbar.menu-b .nav > li:hover > a:active { + color: #fff; + background-color: inherit; + } +} diff --git a/site/common/static/css/fonts/59ZRklaO5bWGqF5A9baEEYX0hVgzZQUfRDuZrPvH3D8.woff2 b/site/common/static/css/fonts/59ZRklaO5bWGqF5A9baEEYX0hVgzZQUfRDuZrPvH3D8.woff2 new file mode 100644 index 0000000..8f3e353 Binary files /dev/null and b/site/common/static/css/fonts/59ZRklaO5bWGqF5A9baEEYX0hVgzZQUfRDuZrPvH3D8.woff2 differ diff --git a/site/common/static/css/fonts/K88pR3goAWT7BTt32Z01m4X0hVgzZQUfRDuZrPvH3D8.woff2 b/site/common/static/css/fonts/K88pR3goAWT7BTt32Z01m4X0hVgzZQUfRDuZrPvH3D8.woff2 new file mode 100644 index 0000000..4d97f3c Binary files /dev/null and b/site/common/static/css/fonts/K88pR3goAWT7BTt32Z01m4X0hVgzZQUfRDuZrPvH3D8.woff2 differ diff --git a/site/common/static/css/fonts/LWCjsQkB6EMdfHrEVqA1KYX0hVgzZQUfRDuZrPvH3D8.woff2 b/site/common/static/css/fonts/LWCjsQkB6EMdfHrEVqA1KYX0hVgzZQUfRDuZrPvH3D8.woff2 new file mode 100644 index 0000000..9fcfff0 Binary files /dev/null and b/site/common/static/css/fonts/LWCjsQkB6EMdfHrEVqA1KYX0hVgzZQUfRDuZrPvH3D8.woff2 differ diff --git a/site/common/static/css/fonts/Qw6_9HvXRQGg5mMbFR3Phn-_kf6ByYO6CLYdB4HQE-Y.woff2 b/site/common/static/css/fonts/Qw6_9HvXRQGg5mMbFR3Phn-_kf6ByYO6CLYdB4HQE-Y.woff2 new file mode 100644 index 0000000..9919a1e Binary files /dev/null and b/site/common/static/css/fonts/Qw6_9HvXRQGg5mMbFR3Phn-_kf6ByYO6CLYdB4HQE-Y.woff2 differ diff --git a/site/common/static/css/fonts/RjgO7rYTmqiVp7vzi-Q5UYX0hVgzZQUfRDuZrPvH3D8.woff2 b/site/common/static/css/fonts/RjgO7rYTmqiVp7vzi-Q5UYX0hVgzZQUfRDuZrPvH3D8.woff2 new file mode 100644 index 0000000..e3d0b43 Binary files /dev/null and b/site/common/static/css/fonts/RjgO7rYTmqiVp7vzi-Q5UYX0hVgzZQUfRDuZrPvH3D8.woff2 differ diff --git a/site/common/static/css/fonts/_P8jt3Y65hJ9c4AzRE0V1OvvDin1pK8aKteLpeZ5c0A.woff2 b/site/common/static/css/fonts/_P8jt3Y65hJ9c4AzRE0V1OvvDin1pK8aKteLpeZ5c0A.woff2 new file mode 100644 index 0000000..5939095 Binary files /dev/null and b/site/common/static/css/fonts/_P8jt3Y65hJ9c4AzRE0V1OvvDin1pK8aKteLpeZ5c0A.woff2 differ diff --git a/site/common/static/css/fonts/cJZKeOuBrn4kERxqtaUH3ZBw1xU1rKptJj_0jans920.woff2 b/site/common/static/css/fonts/cJZKeOuBrn4kERxqtaUH3ZBw1xU1rKptJj_0jans920.woff2 new file mode 100644 index 0000000..402dfd7 Binary files /dev/null and b/site/common/static/css/fonts/cJZKeOuBrn4kERxqtaUH3ZBw1xU1rKptJj_0jans920.woff2 differ diff --git a/site/common/static/css/fonts/u-WUoqrET9fUeobQW7jkRYX0hVgzZQUfRDuZrPvH3D8.woff2 b/site/common/static/css/fonts/u-WUoqrET9fUeobQW7jkRYX0hVgzZQUfRDuZrPvH3D8.woff2 new file mode 100644 index 0000000..d1e66d3 Binary files /dev/null and b/site/common/static/css/fonts/u-WUoqrET9fUeobQW7jkRYX0hVgzZQUfRDuZrPvH3D8.woff2 differ diff --git a/site/common/static/css/fonts/xozscpT2726on7jbcb_pAoX0hVgzZQUfRDuZrPvH3D8.woff2 b/site/common/static/css/fonts/xozscpT2726on7jbcb_pAoX0hVgzZQUfRDuZrPvH3D8.woff2 new file mode 100644 index 0000000..83f534f Binary files /dev/null and b/site/common/static/css/fonts/xozscpT2726on7jbcb_pAoX0hVgzZQUfRDuZrPvH3D8.woff2 differ diff --git a/site/common/static/css/fonts/y7lebkjgREBJK96VQi37Zgt_Rm691LTebKfY2ZkKSmI.woff2 b/site/common/static/css/fonts/y7lebkjgREBJK96VQi37Zgt_Rm691LTebKfY2ZkKSmI.woff2 new file mode 100644 index 0000000..3831ffb Binary files /dev/null and b/site/common/static/css/fonts/y7lebkjgREBJK96VQi37Zgt_Rm691LTebKfY2ZkKSmI.woff2 differ diff --git a/site/common/static/css/fonts/y7lebkjgREBJK96VQi37Zl4sYYdJg5dU2qzJEVSuta0.woff2 b/site/common/static/css/fonts/y7lebkjgREBJK96VQi37Zl4sYYdJg5dU2qzJEVSuta0.woff2 new file mode 100644 index 0000000..776f37d Binary files /dev/null and b/site/common/static/css/fonts/y7lebkjgREBJK96VQi37Zl4sYYdJg5dU2qzJEVSuta0.woff2 differ diff --git a/site/common/static/css/fonts/y7lebkjgREBJK96VQi37ZlBW26QxpSj-_ZKm_xT4hWw.woff2 b/site/common/static/css/fonts/y7lebkjgREBJK96VQi37ZlBW26QxpSj-_ZKm_xT4hWw.woff2 new file mode 100644 index 0000000..79a9963 Binary files /dev/null and b/site/common/static/css/fonts/y7lebkjgREBJK96VQi37ZlBW26QxpSj-_ZKm_xT4hWw.woff2 differ diff --git a/site/common/static/css/fonts/y7lebkjgREBJK96VQi37Zogp9Q8gbYrhqGlRav_IXfk.woff2 b/site/common/static/css/fonts/y7lebkjgREBJK96VQi37Zogp9Q8gbYrhqGlRav_IXfk.woff2 new file mode 100644 index 0000000..1444ec4 Binary files /dev/null and b/site/common/static/css/fonts/y7lebkjgREBJK96VQi37Zogp9Q8gbYrhqGlRav_IXfk.woff2 differ diff --git a/site/common/static/css/fonts/y7lebkjgREBJK96VQi37ZqE8kM4xWR1_1bYURRojRGc.woff2 b/site/common/static/css/fonts/y7lebkjgREBJK96VQi37ZqE8kM4xWR1_1bYURRojRGc.woff2 new file mode 100644 index 0000000..998c545 Binary files /dev/null and b/site/common/static/css/fonts/y7lebkjgREBJK96VQi37ZqE8kM4xWR1_1bYURRojRGc.woff2 differ diff --git a/site/common/static/css/fonts/y7lebkjgREBJK96VQi37ZtDiNsR5a-9Oe_Ivpu8XWlY.woff2 b/site/common/static/css/fonts/y7lebkjgREBJK96VQi37ZtDiNsR5a-9Oe_Ivpu8XWlY.woff2 new file mode 100644 index 0000000..9647a5d Binary files /dev/null and b/site/common/static/css/fonts/y7lebkjgREBJK96VQi37ZtDiNsR5a-9Oe_Ivpu8XWlY.woff2 differ diff --git a/site/common/static/css/fonts/y7lebkjgREBJK96VQi37ZvZraR2Tg8w2lzm7kLNL0-w.woff2 b/site/common/static/css/fonts/y7lebkjgREBJK96VQi37ZvZraR2Tg8w2lzm7kLNL0-w.woff2 new file mode 100644 index 0000000..a4129cc Binary files /dev/null and b/site/common/static/css/fonts/y7lebkjgREBJK96VQi37ZvZraR2Tg8w2lzm7kLNL0-w.woff2 differ diff --git a/site/common/static/css/forum.css b/site/common/static/css/forum.css new file mode 100644 index 0000000..6595ca6 --- /dev/null +++ b/site/common/static/css/forum.css @@ -0,0 +1,1801 @@ +/* sprites */ +.sprite-user-profile, +.sprite-user-profile-settings, +.sprite-subscriptions, +.sprite-display-settings, +.sprite-watched-threads { + border-radius: 50%; + background-color: #e4f0ff; + background-size: 568px 418px; + width: 47px; + height: 47px; +} + +.sprite-user-profile { + background-position: -245px -341px !important; +} +.sprite-user-profile-settings { + background-position: -358px -341px !important; +} + +.sprite-subscriptions { + background-position: -193px -340px; +} + +.sprite-display-settings { + background-position: -406px -340px; +} + +.sprite-watched-threads { + background-position: -306px -340px; +} + +.sprite-printer { + float: right; + height: 36px; + width: 36px; + background-position: 7px -43px; + border: 1px solid #0b4fa0; + margin-left: 5px; + margin-top: 2px; +} + +.btn.default { + margin-top: 2px; +} + +.btn.btn-b { + float: right; + margin-top: 13px; +} + +.sprite-printer:hover { + background-color: #0b4fa0; + background-position: 7px -73px; +} + +.sprite-forum { + background-position: -125px -152px; + width: 15px; + height: 14px; +} + +.sprite-forum-new { + background-position: -100px -152px; + width: 15px; + height: 18px; +} + +.sprite-single-thread { + background-position: -0px -148px; + width: 15px; + height: 18px; +} + +.sprite-new-thread { + background-position: -0px -170px; + width: 15px; + height: 18px; +} + +.sprite-new-replies { + background-position: -24px -170px; + width: 15px; + height: 18px; +} + +.sprite-thread { + background-position: -26px -148px; + width: 15px; + height: 18px; +} + +.sprite-hot-thread { + background-position: -49px -150px; + width: 15px; + height: 18px; +} + +.sprite-locked-thread { + background-position: -72px -150px; + width: 15px; + height: 18px; +} + +.sprite-first-post { + background-position: -200px -150px; + width: 15px; + height: 18px; +} + +.sprite-attachment { + width: 24px; + height: 24px; + background-position: -538px -148px; +} + +.sprite-delete { + background-position: -500px -217px; + width: 15px; + height: 15px; +} + +.sprite-message { + width: 16px; + height: 11px; + background-position: -49px -176px; +} + +.sprite-message-replied { + width: 16px; + height: 16px; + background-position: -89px -171px; +} + +.sprite-message-read { + width: 16px; + height: 16px; + background-position: -69px -171px; +} + +.sprite-message-sent { + width: 16px; + height: 16px; + background-position: -108px -171px; +} + +.sprite-remove-tag { + background-position: -498px -220px; + display: inline-block !important; + width: 14px; + height: 14px; + vertical-align: bottom; + margin-right: 3px; +} + +.tagwidget .sprite-expand { + background-position: -529px -222px; + width: 9px; + height: 9px; +} + +.tagwidget .open .sprite-expand { + background-position: -518px -222px; +} + +.right-arrow { + display: inline-block; + color: #006ae6 !important; + font-size: 16px; +} + +.right-arrow:hover { + text-decoration: none; +} + +.page-header .line { + display: none; +} + +[class*="page-user_"] .page-header .line, +[class*="page-personal_race"] .page-header .line, +[class*="page-lost_password"] .page-header .line, +[class*="page-post_detach"] .page-header .line, +[class*="page-post_move"] .page-header .line, +[class*="page-forum"] .page-header .line, +[class*="page-resend"] .page-header .line, +[class*="page-tag_cloud"] .page-header .line, +.page-post_edit_post .page-header .line, +.page-post_editlog .page-header .line, +.page-login .page-header .line { + display: block; +} + +.tags a { + text-decoration: underline; +} + +.tags a:hover { + text-decoration: none; +} + +.page-user_favourites .page-header .line { + display: none; +} + +.page-forum_view .page-header .line { + display: none; +} + +.toolbar .line { + display: block; + margin: 50px 0 10px 0; +} + +.note { + font-size: 12px; + font-weight: bold; +} + +/* dialog */ +.ui-dialog { + z-index: 1000; +} + +.ui-dialog .ui-dialog-titlebar { + background: #0b4fa0; + color: #fff; +} + +.ui-dialog .ui-button { + outline: none; +} + +.ui-user-widget-overlay { + opacity: .5; + background: #272727; +} + +.page-category_list .ui-dialog, +.page-user_profile_basic .ui-dialog, +[class*=page-post] .ui-dialog { + width: 70% !important; +} + +.page-category_list .ui-dialog .ui-widget-content, +.page-user_profile_basic .ui-dialog .ui-widget-content, +[class*=page-post] .ui-dialog .ui-widget-content { + height: 500px !important; +} + +.page-category_list .sidebar .searchbox { + padding-bottom: 8px; +} + +.nowrap-text { + white-space: nowrap; +} + +/* dtable */ +.dtable .icon { + width: 15px; + padding-right: 0; + padding-left: 2px; + text-align: left; +} + +.page-category_list .dtable .icon { + padding-left: 10px; +} + +.dtable .author, +.dtable .recipient { + width: 60px; +} + +.dtable .views, +.dtable .replies, +.dtable .posts, +.dtable .threads, +.dtable .online { + width: 40px; + text-align: center; +} + +.dtable .markup { + width: 80px; +} + +.dtable .status { + width: 40px; + text-align: center; +} + +.dtable .date, +.dtable .nickname, +.dtable .where, +.dtable .ip { + min-width: 130px; + width: 25%; +} + +.page-search_results .dtable .date, +.page-ip_posts .dtable .date { + width: 110px; + min-width: 0; +} + +.dtable .title { + font-weight: normal; + letter-spacing: .15px; +} + +.dtable .post-mob { + display: none; + vertical-align: middle; +} + +.dtable .post-mob span, +.dtable .postedby { + display: inline-block; + white-space: nowrap; + overflow: hidden; + vertical-align: bottom; +} + +.dtable .postedby { + max-width: 100px; + text-overflow: ellipsis; +} + +.dtable .subject { + font-size: 15px; + padding-right: 0; +} + +.dtable .subject .desc, +.dtable .subject .post-mob { + margin: 0; + padding: 3px 10px; + font-size: 13px; +} + +.dtable .dhead .drow, +.dtable .dhead .subject { + font-weight: normal; + font-size: 13px; +} + +.dtable.form ul { + margin: 0; + padding: 0; + list-style: none; +} + +.dtable.bg .dbody .sticky-thread .dcell { + background: #E4F0FF; +} + +.dtable.form h3 { + font-size: 13px; + margin: 5px 0; + padding: 0; +} + +.dtable.form h3 a { + font-weight: normal; + font-family: 'Open Sans'; +} + +.dtable.form a { + text-decoration: underline; +} + +.dtable.form a:hover { + text-decoration: none; +} + +.dtable.form .drow.category .name { + vertical-align: top; +} + +.ascending, .descending { + font-size: 8px; + padding-left: 3px; +} + +.ascending:before { + content: "\25b2"; +} + +.descending:before { + content: "\25bc"; +} + +.container .dtable .drow:last-child .dcell { + border: 0; +} + +.sticky, .moved { + color: #adadad; + font-style: italic; + font-size: 13px; +} +/* quick launchers */ +.quick-launchers { + width: auto; + margin: 0px auto 20px auto; +} + +.quick-launchers .dcell { + padding: 0; +} + +.quick-launchers a { + font-family: "Open Sans"; + font-size: 12px; + color: #000; + padding: 10px 25px 0 0px; +} + +.quick-launchers a:hover { + text-decoration: none; +} + +.quick-launchers a:hover span:last-child { + color: #0b4fa0; + text-decoration: underline; +} + +.quick-launchers span { + display: inline-block; + vertical-align: middle; +} + +.quick-launchers [class*="sprite-"] { + border-radius: 50%; + background-color: #e4f0ff; + width: 34px; + height: 34px; + margin-right: 3px; +} + +.quick-launchers .sprite-tri { + background-position: -243px -144px; +} + +.quick-launchers .sprite-cla { + background-position: -288px -144px; +} + +.quick-launchers .sprite-lav { + background-position: -340px -144px; +} + +.quick-launchers .sprite-job { + background-position: -392px -145px; +} + +.quick-launchers .sprite-wom { + background-position: -440px -144px; +} + +.quick-launchers .sprite-not { + background-position: -492px -144px; +} + +.simple-form { + padding: 10px 0; +} + +.simple-form select, +.simple-form .btn { + float: left; + vertical-align: middle; + margin: 2px 0; +} + +.simple-form select { + margin-right: 10px; +} + +/* sidebar */ +.sidebar .searchbox { + margin: 0px 0 20px 0; + padding: 0; +} + +.sidebar .searchbox input, +.sidebar .searchbox select { + float: left; + margin-bottom: 10px; + color: #0b4fa0; +} + +.sidebar .searchbox select { + padding-left: 2px; + padding-right: 2px; + font-size: 13px; +} + +.sidebar .searchbox .search-filter { + margin-left: 5px; +} + +.sidebar .searchbox .row { + margin: 0; + padding: 0 0 0 10px; +} + +.sidebar .searchbox .txt, +.sidebar .searchbox .btn { + float: left; +} + +.sidebar .searchbox .txt { + width: 236px; + padding-left: 10px; + padding-right: 5px; + margin-bottom: 0; + margin-right: 5px; +} + +.sidebar .searchbox .search-lnk { + padding-left: 8px; +} + +.sidebar .searchbox.blank { + padding-top: 12px; + margin-bottom: 31px; +} + +.sidebar .searchbox .botbox { + border-right: 0; + height: 10px; + margin: 0px; +} + +.btn-wrapper { + text-align: right; +} + +.btn-wrapper .btn { + margin-left: 5px; +} + +.page-header h1, +.toolbar .paging { + float: left; +} + +.page-header .btn-wrapper, +.toolbar .bt-wrapper { + overflow: hidden; + margin-top: 15px; +} + +.message-page .page-header h1 { + font-size: 30px; +} +.message-page .page-header { + margin-bottom: 10px; +} +.message-page .page-header .btn-wrapper { + min-width: 270px; +} +.toolbar-b .paging { + float: left; + margin-top: 5px; +} + +.btn-wrapper.alone { + float: none; + text-align: right; + padding: 10px 0; +} + +.btn-wrapper.alone .btn { + float: none; +} + +.toolbar { + padding-bottom: 5px; +} + +.profile .dcell { + width: 46%; + padding: 2%; +} + +.profile .botbox { + border-right: 0; +} + +.profile .user-widget { + margin: 25px; + min-height: 120px; +} + +.profile .user-widget h3 { + text-transform: uppercase; +} + +.profile .user-widget h3, +.profile .user-widget p { + overflow: hidden; +} + +.profile [class*="sprite-"] { + display: block; +} + +.profile .edit-lnk { + float: left; + margin-right: 10px; + text-align: center; +} + +.profile .edit-lnk a { + display: inline-block; + margin: 8px auto; + background-color: #0b4fa0; + color: #fff; + font-size: 12px; + padding: 2px 5px; + text-transform: uppercase; +} + +.lnk-actions { + margin: 8px 0; +} + +/* post/message write */ +.row.post-message { + padding: 0; + margin-top: 10px; +} + +.row.post-message .name { + float: none; + display: block; + background: #272727; + color: #fff; + padding: 5px 10px; + margin: 0 0 3px 0; + font-size: 12px; +} + +.row.post-message .value { + height: 350px; + border: 1px solid #adadad; +} + +.row.post-message iframe { + width: 100%; + height: 100%; +} + +.row.post-message textarea.txt { + height: 100%; +} + +.row.post-attachments { + padding: 0; +} + +.row.post-tags .col-6 { + padding: 0; + vertical-align: top; +} + +.row.post-tags .txt { + width: 75%; +} + +.row.post-tags .button-remove { + display: inline-block; + margin: 6px 10px 0 0; + font-weight: bold; + font-size: 14px; + cursor: pointer; +} + +.row.post-attachments { + margin-bottom: 10px; +} + +.row.post-attachments .dcell { + padding: 5px; + font-size: 12px; +} + +.row.post-attachments .dcell.filename { + width: 330px; +} + +.row.post-attachments .dcell.inline { + width: 60px; +} + +.row.post-link-actions { + font-weight: bold; + font-size: 15px; + margin-bottom: 5px; +} + +.post-header-group .row .name, +.page-header-group .row .name { + min-width: 120px; + text-transform: uppercase; + font-weight: bold; +} + +.post-options { + float: left; +} + +.btn-wrapper.post-actions { + overflow: hidden; + margin-top: 10px; +} + +.member-search { + margin: 15px 0; +} + +.alphabet-list b, +.alphabet-list a { + padding-right: 5px; +} + +/* post details */ +.post-header { + padding: 7px 10px; + background-color: #e4f0ff; +} + +.post-actions { + text-align: right; +} + +.post-body-wrapper .post-actions { + margin: 15px 10px; +} + +.post-header .post-actions { + float: right; + font-weight: bold; + font-size: 14px; + margin-top: 2px; +} + +.post-header .post-subject { + float: left; + font-size: 15px; + font-weight: normal; +} + +.root-post .post-header { + border-top: 1px solid #272727; + border-bottom: 1px solid #272727; + background-color: #fff; + padding: 7px 0; +} + +.hidden-post .post-header { + background-color: #f6f6f6; +} + +.root-post .post-header .post-actions { + margin-top: 3px; +} + +.root-post .post-header .post-subject { + font-size: 23px; + font-family: Oswald; + font-weight: normal; +} + +.post-wrapper { + background-color: #f4f8fb; +} + +.post-wrapper .dtable { + margin: 10px 0 20px 0; +} + +.post-wrapper .dtable .dcell { + padding: 2px 12px 4px 12px; + font-size: 14px; +} + +.post-wrapper .drow .dcell:first-child { + width: 180px; + border-right: 1px dotted #adadad; +} + +.post-wrapper .post-notes { + font-size: 12px; + margin-top: 15px; +} + +.post-info .username { + font-weight: bold; + font-size: 15px; + padding-bottom: 8px; +} + +.post-info div { + margin: 3px 0; + font-size: 12px; +} + +.post-preview, .post-reply-view { + font-weight: bold; + padding: 10px 0; + text-transform: uppercase; +} + +.post-body hr { + display: none; +} + +.post-body img { + max-width: 100%; + height: auto; +} + +.post-body .signature-prefix { + height: 1px; + border: 0; + background: #adadad; + margin-top: 15px; + margin-left: 0; + width: 25%; + min-width: 200px; + display: block; +} + +.post-body .reply-block, +.post-body .quote-block { + position: relative; + border: 1px dotted #adadad; + padding: 10px; + margin: 15px 0 10px 0; +} + +.post-body .reply-block .reply-block-title, +.post-body .quote-block .quote-block-title { + position: absolute; + top: -10px; + left: 10px; + background: #fff; + padding: 0 5px; + font-size: 12px; +} + +/* signup */ +.page-user_signup .dtable.form .actions .btn { + float: left; +} + +.page-user_signup .dtable.form .actions .note { + float: left; + margin: 5px 0 0 10px; +} + +/* user profile */ +.dtable.form .images .col-6 { + width: 49%; +} + +.dtable.form .images .col-6:last-child { + float: right; +} + +.dtable.form .images .image-holder { + float: left; + margin-bottom: 5px; +} + +.dtable.form .images .image-upload { + overflow: hidden; + display: block; + padding-left: 5px; +} + +.dtable.form .images .file { + display: block; + width: 98%; + margin: 7px 0; +} + +.page-user_profile_basic .dtable.form .actions .dcell:last-child { + text-align: center; +} + +.dtable.form .social-sm .network { + position: relative; + display: inline-block; + width: 24.7%; + min-width: 290px; + padding: 0 0 8px 0; + text-align: right; +} + +.dtable.form .social-sm .txt { + width: 85%; + min-width: 250px; + margin-left: 15%; +} + +.dtable.form .social-sm span { + position: absolute; + top: 0px; + left: 0; + width: 33px; + height: 33px; +} + +.dtable.form .social-sm .sprite-twitter { + background-position: -138px -42px; +} + +.dtable.form .social-sm .sprite-vimeo { + background-position: -190px -44px; +} + +.dtable.form .social-sm .sprite-tumblr { + background-position: -280px -44px; +} + +.dtable.form .social-sm .sprite-facebook { + background-position: -228px -42px; +} + +.dtable.form .social-sm .sprite-youtube { + background-position: -336px -42px; + margin-right: 0px; +} + +.dtable.form .social-sm .sprite-yahoo { + background-position: -140px -70px; +} + +.dtable.form .social-sm .sprite-msn { + background-position: -138px -42px; +} + +.dtable.form .social-sm .sprite-icq { + background-position: -186px -74px; +} + +.dtable.form .social-sm .sprite-aim { + background-position: -219px -76px; +} + +/* user view */ +.account-info { + padding-top: 10px; +} + +.account-info .dtable.form .value { + color: #0b4fa0; +} + +[class*=account-] .dtable.form .name { + width: 15%; + min-width: 60px; +} + +[class*=account-] .btn-wrapper { + text-align: center; + padding-top: 8px; +} + +[class*=account-] .btn-wrapper .btn { + float: none; +} + +[class*=account-] .title { + font-weight: bold; + font-size: 15px; + margin-bottom: 8px; +} + +.account-races .race-name { + color: #0b4fa0; +} + +.account-bikes { + padding-top: 35px; +} + +.account-coordinates .tria-view .title, +.account-coordinates .road-view .title { + text-transform: uppercase; + color: #006ae6; + font-size: 14px; +} + +.account-coordinates img { + max-width: 412px; + width: 80%; +} + +.account-bikes .bike-info { + float: left; + width: 50%; + max-width: 300px; +} + +.account-bikes .row .btn-wrapper { + overflow: hidden; + text-align: left; + padding: 0; + margin: 54px 0 0 5px; +} + +.account-bikes .bike-details { + padding-top: 5px; + display: none; +} + +.account-bikes .bike-details p { + clear: left; + padding: 0; + margin: 2px 0; +} + +.account-bikes .bike-details p em { + font-style: normal; + color: #006ae6; +} + +.account-bikes .bike-details p span { + float: left; +} + +.account-bikes .bike-details p span:last-child { + padding-left: 2px; +} + +.account-bikes .bike-form { + position: relative; + display: none; + padding: 0 20px; + border: 1px solid #0b4fa0; + background-color: #fff; + margin-bottom: 10px; +} + +.account-bikes .bike-form .sprite-close { + position: absolute; + width: 14px; + height: 14px; + background-position: -498px -220px; + left: -6px; + top: -6px; +} + +.account-bikes .bike-form .road-bike .drow.tri { + display: none; +} + +.account-bikes .bike-form .dtable.form .name { + width: 30%; + max-width: 180px; +} + +/* login */ +.page-login .dtable.form { + width: 70%; + margin: 50px auto 0 auto; +} + +.page-login .form .txt { + width: 90%; +} + +.page-login .form .checkbox { + float: left; + margin-top: 10px; + margin-right: 5px; +} + +.page-login .form label { + font-size: 13px; +} + +.page-login .actions .lostpasswd, +.page-login .actions .btn { + float: left; +} + +.page-login .actions .lostpasswd { + margin: 10px 0 0 10px; +} + +.page-login .dtable.form .name { + width: 30%; +} + +.page-login .dtable.form .actions .dcell:last-child { + padding: 0; + text-align: left; +} + +.page-login .remember-me label { + font-size: 10px; + line-height: 11px !important; + vertical-align: middle; +} + +.page-login .no-account { + display: block; + background-color: #e4f0ff; + width: 90%; + margin: 30px auto; + padding: 20px 0; + min-width: 310px; + max-width: 500px; + margin-top: 60px; +} + +.page-login .no-account .col-4 { + text-align: center; + padding-top: 15px; +} + +.page-login .no-account .sprite-logo { + float: none; + margin-left: 10px; +} + +.page-login .botbox { + display: none; + height: 15px; + border-right: 0; +} + +/* search results */ +.page-search_results .searchbox { + margin: 20px 0; +} + +.page-search_results .searchbox input[type=radio] { + float: left; +} + +.page-search_results .searchbox label { + display: block; + overflow: hidden; + margin-right: 10px; +} + +.page-search_results .searchbox .option { + float: left; + display: inline-block; + white-space: inherit; +} + +.page-search_results .searchbox .option:first-child { + width: 47%; +} + +.page-search_results .searchbox .option:nth-child(2) { + width: 35%; +} + +.page-search_results .searchbox .option:last-child { + width: 18%; +} + +.page-search_results .searchbox .col-4:first-child { + width: 35%; +} + +.page-search_results .searchbox .col-4:nth-child(2) { + width: 55%; +} + +.page-search_results .searchbox .col-4:last-child { + width: 10%; + text-align: right; +} + +.ad { + margin: 15px 0 5px 0; + text-align: center; +} + +.adbox .ad { + margin: 0; +} + +.ad h4 { + margin: 0; + color: #adadad; + font-weight: normal; + font-size: 11px; + font-family: 'Open Sans'; + padding-bottom: 0; + text-align: center; +} + +.admob, +.adtab { + display: none; + text-align: center; +} + +.adtab { + margin: 40px 0 20px 0; +} + +.mobpoll { + display: none; + margin-right: 20px; +} +.mobpoll .widget { + padding: 0; +} +.mobpoll .widget-divider { + display: none; +} +.mobpoll .btn { + color: #fff; +} + +.pm-notification { + display: inline-block; + border-radius: 50%; + background-color: #0b4fa0; + width: 19px; + height: 19px; + color: #fff; + text-align: center; + vertical-align: middle; + font-size: 12px !important; +} + +/* hot topics */ +.widget { + position: relative; +} +.toolbar-b .widget.hot-topics { + float: left; + display: inline-block; + min-width: 301px; +} +.toolbar-b .widget.hot-topics .hotlnk .sprite-arrow-down-b { + margin-left: 10px; +} +.tagwidget { + display: none; + position: absolute; + width: 296px; + background: #e4f0ff; + top: 30px; + left: 0; + z-index: 10; + border: 2px solid #e4f0ff; + box-shadow: 0px 0px 10px 0px #a4a4a4; +} +.tagwidget ul { + list-style: none; + margin: 0; + padding: 0; +} +.tagwidget a { + display: inline-block; + cursor: pointer; + padding: 5px; + text-decoration: none; +} +.tagwidget .children { + background: #fff; + display: none; + max-height: 300px; + overflow-x: auto; +} +.tagwidget > ul > li > a { + position: relative; + display: block; + padding: 11px 10px; + font-weight: bold; + font-size: 14px; + border-bottom: 1px solid #fff; +} +.tagwidget > ul > li:last-child > a { + border: 0; +} +.tagwidget > ul > li > a [class*="sprite-"] { + display: block; + right: 10px; + top: 15px; +} +.tagwidget li ul { + padding: 0 10px; +} +.tagwidget .children li > ul { + padding: 0; + margin: 5px 0; +} +.tagwidget .children > ul > li > a { + display: block; + border-bottom: 1px dotted #0b4fa0; + font-weight: bold; + text-align: center; + font-size: 14px; +} +.tagwidget .thread span { + display: block-inline; + float: left; + margin-top: 3px; +} +.tagwidget .thread a { + display: block; + overflow: hidden; +} +.tagwidget .thread a:hover { + text-decoration: underline; +} +.widget.hot-topics { + padding: 0; +} +.hot-topics .hotlnk { + display: block; + background: #0b4fa0; + padding: 5px 10px; + color: #fff; + font-family: Oswald; + font-size: 13px; + text-transform: uppercase; + letter-spacing: 1.20px; +} +.hot-topics .hotlnk:hover { + text-decoration: none; +} +.hot-topics .hotlnk .sprite-arrow-down-b { + float: right; + margin-top: 5px; +} +@media (max-width: 1350px) { + .quick-launchers a { + padding-right: 10px; + } +} + +@media (max-width: 1230px) { + .page-login .col-7, + .page-login .col-5, + .page-search .col-6 { + float: none; + width: auto; + } + + .page-login .no-account { + width: 90%; + min-width: 480px; + max-width: none; + } + + .page-login .dotted { + display: none; + } + + .page-login .botbox { + display: block; + } + + /* user profile */ + .dtable.form .images .col-6, + .dtable .txt.shotext, + .dtable .txt.midtext { + width: 47.5%; + min-width: 260px; + } + + .dtable .subject, + .dtable .right-arrow { + font-size: 14px; + } + + .page-search .dtable.form .txt.midtext { + width: 80%; + } + + .post-body img { + max-width: 80%; + height: auto; + } + + .quick-launchers { + float: right; + } + + .quick-launchers a:last-child { + padding-right: 0; + padding-left: 10px; + } + + .quick-launchers a span:last-child { + display: none; + } +} + +@media (max-width: 1095px) { + .dtable .views { + display: none; + } + + /* user profile */ + .dtable.form .images .col-6 { + width: 47.5%; + min-width: 240px; + } + + .dtable.form .txt.shotext, + .dtable.form .txt.midtext { + min-width: 240px; + } + + .dtable.form .social-sm .network { + width: 49.5%; + } +} + + +/* tablet portrait and landscape */ +@media (max-width: 1023px) { + .dtable .where { + display: none; + } + + .wallpaper { + background-image: none; + } + + .dtable .views { + display: table-cell; + } + + .post-body { + font-size: 15px; + } + + .page-search_results .searchbox .col-4:first-child, + .page-search_results .searchbox .col-4:last-child { + float: none; + width: auto; + display: block; + } + + .page-search_results .searchbox .col-4:first-child { + padding-bottom: 10px; + } + + .page-search_results .searchbox .col-4:nth-child(2) { + width: 100%; + } + + .page-search_results .searchbox .btn { + margin-top: 10px; + } + + .page-search_results .searchbox .txt.midtext { + min-width: 0; + width: 100%; + } + + .page-user_view .col-6 { + float: none; + width: 100%; + } + + .dtable.form .actions .dcell:last-child, + .page-user_profile_basic .dtable.form .actions .dcell:last-child { + text-align: left; + } + + .adtab, + .page-post_view_flat .admob { + display: block; + } + + .admob .adwrapper { + border-top: 1px dotted #adadad; + padding-top: 20px; + } + + .post-body img { + max-width: 80%; + height: auto; + } + + .btn-wrapper .sprite-printer { + display: none; + } + + .toolbar-b .paging { + margin-bottom: 10px; + } +} + +@media (max-width: 1023px) { + .dtable .views { + display: none; + } +} + +@media (max-width: 767px) { + .quick-launchers { + display: none; + } + + .page-forum_view .toolbar .btn-wrapper { + display: none; + } + + /* user profile */ + .dtable.form .name, + .dtable.form .divider { + padding-right: 7px; + } + + .dtable.form .images .col-6, + .dtable.form .images .col-6:last-child, + .row.post-tags .col-6 { + float: none; + width: 98%; + padding: 0; + } + + .post-body-wrapper .post-actions { + margin: 10px 10px 18px 10px; + } + + .toolbar { + padding: 0; + margin: 20px 0 10px 0; + } + + .toolbar .paging, + .toolbar-b .paging { + position: relative; + display: block; + width: 100%; + padding: 5px 0px 10px 0; + background-color: #e4f0ff; + text-align: center; + font-size: 20px; + font-family: Oswald; + margin-bottom: 15px; + } + + .paging select { + font-size: 13px; + padding: 3px 1px; + text-align: center; + text-align-last: center; + } + + .paging a { + display: inline; + } + + .paging select, + .paging span { + display: inline-block; + vertical-align: middle; + } + + .paging .next, + .paging .last, + .paging .prev, + .paging .first { + position: absolute; + top: 5px; + } + + .paging .prev { + left: 30px; + } + + .paging .first { + left: 10px; + } + + .paging .next { + right: 30px; + } + + .paging .last { + right: 10px; + } + + .page-post_view_flat .toolbar .btn-wrapper { + overflow: visible; + text-align: left; + } + + .page-post_view_flat .toolbar .btn-wrapper .btn { + margin: 0; + } + + .hot-topics .hotlnk { + margin: 5px 0 10px 0; + } + + .dtable .subject { +/* min-width: 0; + max-width: 0;*/ + } +} + +@media (max-width: 695px) { + .quick-launchers, + .dtable .author, + .dtable .from, + .dtable .views, + .dtable .replies, + .dtable .threads, + .dtable .posts, + .dtable .date, + .dtable .forum, + .dtable .nickname, + .btn-wrapper .btn-new-post, + .post-wrapper .post-views { + display: none; + } + + .page-forum_view h1, + .page-forum_view .btn-wrapper { + float: none; + } + + .page-forum_view .btn-wrapper .btn { + display: block; + float: left; + margin-right: 9px; + } + + .dtable .post-mob, + .mobpoll { + display: block; + } + + .dtable .postedby { + max-width: 80px; + } + + /* post view flat */ + .post-wrapper .dtable, + .post-wrapper .drow .dcell, + .post-wrapper .drow { + display: block; + padding: 0; + margin: 0; + } + + .post-wrapper .drow .dcell:first-child { + width: auto; + border: 0; + } + + .root-post .post-header { + border-top: 2px solid #272727; + margin: 10px 10px 0 10px; + } + + .root-post .post-header .post-subject { + line-height: 26px; + } + + .post-header .post-actions { + display: none; + } + + .post-info { + border-bottom: 2px dotted #272727; + padding: 0 0 10px 0; + margin: 5px 10px 10px 10px; + } + + .post-info div { + display: inline; + padding-right: 5px; + } + + .post-body img { + max-width: 100%; + height: auto; + } + + .post-body, + .post-notes { + padding: 0 10px; + } + + .page-header h1 { + float: none; + } + + .page-header .btn-wrapper { + margin: 0 0 15px 0; + } + + .page-header .btn-wrapper .btn { + float: left; + margin-left: 0px; + margin-right: 5px; + } + + .page-search_results .page-header .btn-wrapper { + display: none; + } + + .profile .dcell { + display: table-row; + } + + .dhead .dcell { + font-size: 13px; + padding-top: 0px; + padding-bottom: 1px; + } + + .dtable .subject .title { + letter-spacing: .5px; + } + + /* login form */ + .page-login .dtable.form { + width: 70%; + margin: 10px auto 0 auto; + } + + .page-login .no-account { + margin: 10px auto 30px auto; + padding: 10px 0 20px 0; + } + + .page-login .dtable.form { + width: 100%; + } + + .page-login .dtable.form .name { + width: 100px; + } + + .page-header h1 { + padding-bottom: 0; + } + + .page-header .btn-wrapper { + margin-bottom: 5px + } + + .page-login .no-account .col-4 { + max-width: 120px; + width: 24%; + } + + .page-login .no-account .col-8 { + width: 76%; + } + + .page-login .no-account .sprite-logo { + margin-left: 0px; + } + + .page-post_view_flat .page-header { + margin: 0px 10px; + } + + .page-post_view_flat .page-header .line { + display: none; + } + + .page-post_view_flat .content { + margin: 0; + } + + /* ad */ + .adtab { + display: none; + } + + .admob { + display: block; + } + + .admob .adwrapper { + margin: 0 10px; + } + + .page-post_view_flat .toolbar .btn-wrapper .btn { + margin-left: 10px; + } +} diff --git a/site/common/static/css/images/close.png b/site/common/static/css/images/close.png new file mode 100644 index 0000000..20baa1d Binary files /dev/null and b/site/common/static/css/images/close.png differ diff --git a/site/common/static/css/images/loading.gif b/site/common/static/css/images/loading.gif new file mode 100644 index 0000000..5087c2a Binary files /dev/null and b/site/common/static/css/images/loading.gif differ diff --git a/site/common/static/css/images/loadingAnimation.gif b/site/common/static/css/images/loadingAnimation.gif new file mode 100644 index 0000000..82290f4 Binary files /dev/null and b/site/common/static/css/images/loadingAnimation.gif differ diff --git a/site/common/static/css/images/m1.png b/site/common/static/css/images/m1.png new file mode 100644 index 0000000..329ff52 Binary files /dev/null and b/site/common/static/css/images/m1.png differ diff --git a/site/common/static/css/images/m2.png b/site/common/static/css/images/m2.png new file mode 100644 index 0000000..b999cbc Binary files /dev/null and b/site/common/static/css/images/m2.png differ diff --git a/site/common/static/css/images/m3.png b/site/common/static/css/images/m3.png new file mode 100644 index 0000000..9f30b30 Binary files /dev/null and b/site/common/static/css/images/m3.png differ diff --git a/site/common/static/css/images/m4.png b/site/common/static/css/images/m4.png new file mode 100644 index 0000000..0d3f826 Binary files /dev/null and b/site/common/static/css/images/m4.png differ diff --git a/site/common/static/css/images/m5.png b/site/common/static/css/images/m5.png new file mode 100644 index 0000000..61387d2 Binary files /dev/null and b/site/common/static/css/images/m5.png differ diff --git a/site/common/static/css/images/next.png b/site/common/static/css/images/next.png new file mode 100644 index 0000000..08365ac Binary files /dev/null and b/site/common/static/css/images/next.png differ diff --git a/site/common/static/css/images/prev.png b/site/common/static/css/images/prev.png new file mode 100644 index 0000000..329fa98 Binary files /dev/null and b/site/common/static/css/images/prev.png differ diff --git a/site/common/static/css/images/sprite.png b/site/common/static/css/images/sprite.png new file mode 100644 index 0000000..d3db4ea Binary files /dev/null and b/site/common/static/css/images/sprite.png differ diff --git a/site/common/static/css/images/sprite.svg b/site/common/static/css/images/sprite.svg new file mode 100644 index 0000000..92e7f49 --- /dev/null +++ b/site/common/static/css/images/sprite.svg @@ -0,0 +1,1246 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/site/common/static/css/images/star-matrix.gif b/site/common/static/css/images/star-matrix.gif new file mode 100644 index 0000000..bba8938 Binary files /dev/null and b/site/common/static/css/images/star-matrix.gif differ diff --git a/site/common/static/css/images/ui-icons_444444_256x240.png b/site/common/static/css/images/ui-icons_444444_256x240.png new file mode 100644 index 0000000..a802263 Binary files /dev/null and b/site/common/static/css/images/ui-icons_444444_256x240.png differ diff --git a/site/common/static/css/images/ui-icons_555555_256x240.png b/site/common/static/css/images/ui-icons_555555_256x240.png new file mode 100644 index 0000000..7009bf7 Binary files /dev/null and b/site/common/static/css/images/ui-icons_555555_256x240.png differ diff --git a/site/common/static/css/images/ui-icons_777777_256x240.png b/site/common/static/css/images/ui-icons_777777_256x240.png new file mode 100644 index 0000000..8e26ee4 Binary files /dev/null and b/site/common/static/css/images/ui-icons_777777_256x240.png differ diff --git a/site/common/static/css/jquery-ui.min.css b/site/common/static/css/jquery-ui.min.css new file mode 100644 index 0000000..b842845 --- /dev/null +++ b/site/common/static/css/jquery-ui.min.css @@ -0,0 +1,7 @@ +/*! jQuery UI - v1.11.4 - 2016-02-22 +* http://jqueryui.com +* Includes: core.css, draggable.css, resizable.css, selectable.css, sortable.css, accordion.css, autocomplete.css, button.css, datepicker.css, dialog.css, menu.css, progressbar.css, selectmenu.css, slider.css, spinner.css, tabs.css, tooltip.css, theme.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&fwDefault=normal&cornerRadius=3px&bgColorHeader=e9e9e9&bgTextureHeader=flat&borderColorHeader=dddddd&fcHeader=333333&iconColorHeader=444444&bgColorContent=ffffff&bgTextureContent=flat&borderColorContent=dddddd&fcContent=333333&iconColorContent=444444&bgColorDefault=f6f6f6&bgTextureDefault=flat&borderColorDefault=c5c5c5&fcDefault=454545&iconColorDefault=777777&bgColorHover=ededed&bgTextureHover=flat&borderColorHover=cccccc&fcHover=2b2b2b&iconColorHover=555555&bgColorActive=007fff&bgTextureActive=flat&borderColorActive=003eff&fcActive=ffffff&iconColorActive=ffffff&bgColorHighlight=fffa90&bgTextureHighlight=flat&borderColorHighlight=dad55e&fcHighlight=777620&iconColorHighlight=777620&bgColorError=fddfdf&bgTextureError=flat&borderColorError=f1a899&fcError=5f3f3f&iconColorError=cc0000&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=666666&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=5px&offsetTopShadow=0px&offsetLeftShadow=0px&cornerRadiusShadow=8px +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-draggable-handle{-ms-touch-action:none;touch-action:none}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable{-ms-touch-action:none;touch-action:none}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-sortable-handle{-ms-touch-action:none;touch-action:none}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin:2px 0 0 0;padding:.5em .5em .5em .7em;min-height:0;font-size:100%}.ui-accordion .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-icons .ui-accordion-icons{padding-left:2.2em}.ui-accordion .ui-accordion-header .ui-accordion-header-icon{position:absolute;left:.5em;top:50%;margin-top:-8px}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-button{display:inline-block;position:relative;padding:0;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:normal}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon,.ui-button-icons-only .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary,.ui-button-icons-only .ui-button-icon-primary{left:.5em}.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:45%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-dialog{overflow:hidden;position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:12px;height:12px;right:-5px;bottom:-5px;background-position:16px 16px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:none}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{position:relative;margin:0;padding:3px 1em 3px .4em;cursor:pointer;min-height:0;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");height:100%;filter:alpha(opacity=25);opacity:0.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-selectmenu-menu{padding:0;margin:0;position:absolute;top:0;left:0;display:none}.ui-selectmenu-menu .ui-menu{overflow:auto;overflow-x:hidden;padding-bottom:1px}.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup{font-size:1em;font-weight:bold;line-height:1.5;padding:2px 0.4em;margin:0.5em 0 0 0;height:auto;border:0}.ui-selectmenu-open{display:block}.ui-selectmenu-button{display:inline-block;overflow:hidden;position:relative;text-decoration:none;cursor:pointer}.ui-selectmenu-button span.ui-icon{right:0.5em;left:auto;margin-top:-8px;position:absolute;top:50%}.ui-selectmenu-button span.ui-selectmenu-text{text-align:left;padding:0.4em 2.1em 0.4em 1em;display:block;line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default;-ms-touch-action:none;touch-action:none}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;padding:0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:22px}.ui-spinner-button{width:16px;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top:none;border-bottom:none;border-right:none}.ui-spinner .ui-icon{position:absolute;margin-top:-8px;top:50%;left:0}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-spinner .ui-icon-triangle-1-s{background-position:-65px -16px}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px;-webkit-box-shadow:0 0 5px #aaa;box-shadow:0 0 5px #aaa}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #ddd;background:#fff;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #ddd;background:#e9e9e9;color:#333;font-weight:bold}.ui-widget-header a{color:#333}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #c5c5c5;background:#f6f6f6;font-weight:normal;color:#454545}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#454545;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #ccc;background:#ededed;font-weight:normal;color:#2b2b2b}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited{color:#2b2b2b;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #003eff;background:#007fff;font-weight:normal;color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #dad55e;background:#fffa90;color:#777620}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#777620}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #f1a899;background:#fddfdf;color:#5f3f3f}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#5f3f3f}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#5f3f3f}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-state-default .ui-icon{background-image:url("images/ui-icons_777777_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url("images/ui-icons_555555_256x240.png")}.ui-state-active .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-state-highlight .ui-icon{background-image:url("images/ui-icons_777620_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cc0000_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:3px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:3px}.ui-widget-overlay{background:#aaa;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{margin:0 0 0 0;padding:5px;background:#666;opacity:.3;filter:Alpha(Opacity=30);border-radius:8px} \ No newline at end of file diff --git a/site/common/static/css/lightbox.min.css b/site/common/static/css/lightbox.min.css new file mode 100644 index 0000000..25deb45 --- /dev/null +++ b/site/common/static/css/lightbox.min.css @@ -0,0 +1 @@ +.lb-loader,.lightbox{text-align:center;line-height:0}body:after{content:url(images/close.png) url(images/loading.gif) url(images/prev.png) url(images/next.png);display:none}.lb-dataContainer:after,.lb-outerContainer:after{content:"";clear:both}body.lb-disable-scrolling{overflow:hidden}.lightboxOverlay{position:absolute;top:0;left:0;z-index:9999;background-color:#000;filter:alpha(Opacity=80);opacity:.8;display:none}.lightbox{position:absolute;left:0;width:100%;z-index:10000;font-weight:400}.lightbox .lb-image{display:block;height:auto;max-width:inherit;border-radius:3px}.lightbox a img{border:none}.lb-outerContainer{position:relative;background-color:#fff;width:250px;height:250px;margin:0 auto;border-radius:4px}.lb-loader,.lb-nav{position:absolute;left:0}.lb-outerContainer:after{display:table}.lb-container{padding:4px}.lb-loader{top:43%;height:25%;width:100%}.lb-cancel{display:block;width:32px;height:32px;margin:0 auto;background:url(images/loading.gif) no-repeat}.lb-nav{top:0;height:100%;width:100%;z-index:10}.lb-container>.nav{left:0}.lb-nav a{outline:0;background-image:url(data:image/gif;base64,R0lGODlhAQABAPAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==)}.lb-next,.lb-prev{height:100%;cursor:pointer;display:block}.lb-nav a.lb-prev{width:34%;left:0;float:left;background:url(images/prev.png) left 48% no-repeat;filter:alpha(Opacity=0);opacity:0;-webkit-transition:opacity .6s;-moz-transition:opacity .6s;-o-transition:opacity .6s;transition:opacity .6s}.lb-nav a.lb-prev:hover{filter:alpha(Opacity=100);opacity:1}.lb-nav a.lb-next{width:64%;right:0;float:right;background:url(images/next.png) right 48% no-repeat;filter:alpha(Opacity=0);opacity:0;-webkit-transition:opacity .6s;-moz-transition:opacity .6s;-o-transition:opacity .6s;transition:opacity .6s}.lb-nav a.lb-next:hover{filter:alpha(Opacity=100);opacity:1}.lb-dataContainer{margin:0 auto;padding-top:5px;width:100%;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px}.lb-dataContainer:after{display:table}.lb-data{padding:0 4px;color:#ccc}.lb-data .lb-details{width:85%;float:left;text-align:left;line-height:1.1em}.lb-data .lb-caption{font-size:13px;font-weight:700;line-height:1em}.lb-data .lb-number{display:block;clear:left;padding-bottom:1em;font-size:12px;color:#999}.lb-data .lb-close{display:block;float:right;width:30px;height:30px;background:url(images/close.png) top right no-repeat;text-align:right;outline:0;filter:alpha(Opacity=70);opacity:.7;-webkit-transition:opacity .2s;-moz-transition:opacity .2s;-o-transition:opacity .2s;transition:opacity .2s}.lb-data .lb-close:hover{cursor:pointer;filter:alpha(Opacity=100);opacity:1} diff --git a/site/common/static/css/print.css b/site/common/static/css/print.css new file mode 100644 index 0000000..13cc46f --- /dev/null +++ b/site/common/static/css/print.css @@ -0,0 +1,9 @@ +.header, +.footer, +.breadcrumb, +.sidebar, +.quick-launchers, +.btn-wrapper, +.toolbar { + display: none; +} diff --git a/site/common/static/css/print.min.css b/site/common/static/css/print.min.css new file mode 100644 index 0000000..1c06c36 --- /dev/null +++ b/site/common/static/css/print.min.css @@ -0,0 +1 @@ +.header,.footer,.breadcrumb,.sidebar,.quick-launchers,.btn-wrapper,.toolbar{display:none} \ No newline at end of file diff --git a/site/common/static/css/style.css b/site/common/static/css/style.css new file mode 100644 index 0000000..e2b18d1 --- /dev/null +++ b/site/common/static/css/style.css @@ -0,0 +1,2734 @@ +/* + .section-static.section-reverse { + margin-left: 0; + margin-right: 0; + } + + Simple Grid + Project Page - http://thisisdallas.github.com/Simple-Grid/ + Author - Dallas Bass + Site - http://ninenineteen.co +*/ + +*, *:after, *:before { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + +body { + margin: 0; +} + +.ie8 > .container { + min-width: 990px; +} + +.ie8 .home-featured { + margin-bottom: 25px; +} +.ie8 .sidebar { + padding-top: 0; + _width: 300px; + width: 330px; +} + +.ie8 [class*="sprite-"] { + background-image: url( "../images/sprite.png"); +} + +[class*='col-'] { + float: left; + min-height: 1px; +} + +.header .mashead-b { + height: 154px; +} + +.footer [class*='col-'] { + padding-right: 0; +} + +.grid { + position: relative; + width: 100%; + max-width: 1140px; + /*min-width: 748px; /* when using padded grid on ipad in portrait mode, width should be viewport-width - padding = (768 - 20) = 748. actually, it should be even smaller to allow for padding of grid containing element */*/ + margin: 0 auto; + overflow: hidden; +} + +.list-view:after, +.grid:after { + content: ""; + display: table; + clear: both; +} + +.list-view:before { + content: ""; + display: table; +} + +.grid-pad { + padding-top: 20px; + padding-left: 20px; /* grid-space to left */ + padding-right: 0; /* grid-space to right: (grid-space-left - column-space) e.g. 20px-20px=0 */ +} +.list-view.archive h3 { + font-size: 20px; + font-weight: 500; + font-family: 'Oswald', sans-serif; + padding: 0 0 5px 0; +} +.push-right { + float: right; +} + +/* Content Columns */ + +.col-1-1 { + width: 100%; +} +.col-2-3, .col-8-12 { + width: 66.66%; +} + +.col-1-2, .col-6-12 { + width: 50%; +} + +.col-1-3, .col-4-12 { + width: 33.33%; +} + +.col-1-4, .col-3-12 { + width: 25%; +} + +.col-1-5 { + width: 20%; +} + +.col-1-6, .col-2-12 { + width: 16.667%; +} + +.col-1-7 { + width: 14.28%; +} + +.col-1-8 { + width: 12.5%; +} + +.col-1-9 { + width: 11.1%; +} + +.col-1-10 { + width: 10%; +} + +.col-1-11 { + width: 9.09%; +} + +.col-1-12 { + width: 8.33% +} + +/* Layout Columns */ + +.col-11-12 { + width: 91.66% +} + +.col-10-12 { + width: 83.333%; +} + +.col-9-12 { + width: 75%; +} + +.col-5-12 { + width: 41.66%; +} + +.col-7-12 { + width: 58.33% +} + +/* Pushing blocks */ + +.push-2-3, .push-8-12 { + margin-left: 66.66%; +} + +.push-1-2, .push-6-12 { + margin-left: 50%; +} + +.push-1-3, .push-4-12 { + margin-left: 33.33%; +} + +.push-1-4, .push-3-12 { + margin-left: 25%; +} + +.push-1-5 { + margin-left: 20%; +} + +.push-1-6, .push-2-12 { + margin-left: 16.667%; +} + +.push-1-7 { + margin-left: 14.28%; +} + +.push-1-8 { + margin-left: 12.5%; +} + +.push-1-9 { + margin-left: 11.1%; +} + +.push-1-10 { + margin-left: 10%; +} + +.push-1-11 { + margin-left: 9.09%; +} + +.push-1-12 { + margin-left: 8.33% +} + +.col-fullwidth { + padding: 0; +} + +.coaching .dcell { + border: 1px solid #ccc; +} +table { + border-collapse: collapse; + width: 100%; +} +td, th { + border: 1px solid #ccc; +} +th { + font-weight: bold; + font-size: 15px; +} +td.brand { + border-left: 0; + border-right: 0; + font-size: 15px; + font-weight: bold; + padding: 5px 0; +} + +.social-networks { + margin: 10px 0 5px 0; +} + +.social-networks * { + float: left !important; + margin-right: 5px !important; +} +/* Core Overrides and Main Site */ + +/* COLORS */ +.medium-blue { + color: #0b4fa0; +} +.bright-blue { + color: #006ae6; +} +.bright-blue-highlight { + color: #268aff; +} +.light-blue { + color: #e4f0ff; +} +.light-grey { + color: #adadad +} +.medium-grey { + color: #4f4f4f; +} +.dark-grey { + color: #272727; +} +.black { + color: #000; +} + + + +/* the default rating is placed as a background image in the ul */ +/* use the background position according to the table above to display the required images*/ +.rated { + display: block; + width: 80px; + height: 16px; + overflow: hidden; + margin-top: 7px; +} +.rating-table { + display: block; + margin-bottom: 15px; +} +.rating-table > div { + display: inline-block; + width: 24%; + height: 18px; + padding: 0 10px 0 0; +} +.nostar img { + margin-top: 0; +} +.onestar img { + margin-top: -16px; +} +.twostar img { + margin-top: -32px; +} +.threestar img { + margin-top: -48px; +} +.fourstar img { + margin-top: -64px; +} +.fivestar img { + margin-top: -80px; +} + +.container-shadow { + display: block; + position: fixed; + top: 0; + z-index: -100; + width: 100%; + max-width: 990px; + height: 100%; + left: 50%; + margin-left: -495px; + box-shadow: 0 0 35px 5px #B0B0B0; +} + +.container-bg { + display: block; + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: -1; + text-align: center; + overflow: hidden; +} + +.container-bg img.bg-image { + display: block; + position: relative; + left: 50%; + margin-left: -952px; + height: auto; + width: 1920px; +} + +/* LINKS */ + +a.no-underline { + text-decoration: none; + color: initial; +} +h1,h2,h3, +h1 a, h2 a, h3 a { + color: #000; + line-height: 1.15; +} +.content { + position: relative; +} +.section h1 { + padding: 0 0 25px 0; +} +.section .center h1 { + padding: 0 0 5px 0; +} +.section.listings h2 { + font-weight: 400; +} +.section.article h1 { + padding: 0 0 15px 0; +} +.section.list h3 { + line-height: 1.3; +} +.section.list .advert-small { + margin-bottom: 25px; +} +.category-list h1 { + padding: 0 0 15px 0; +} +.list-block h3, +.list-block h3 a{ + font-size: 15px; + color: #0b4fa0; + font-weight: 700; + font-family: 'Open Sans', sans-serif; +} + +.list-block h3 a { + cursor: pointer; +} + +.list-block .col-1-2:nth-child(2) { + width: 49%; + padding-right: 1%; +} + + +hr { + border-top: 1px solid #adadad; + display: block; + width: 100%; + height: 0; + margin-bottom: 20px; +} + +hr.line { + margin-bottom: 20px; +} + +blockquote { + font-size: 20px; + font-style: italic; + font-weight: 400; + line-height: 1.6; + width: 50%; + margin: 0 0 0 25px; + float: right; + clear: left; + margin-bottom: 20px; + background-color: #ADD8E6; +} +blockquote div { + text-align: justify; +} + +blockquote.left { + margin: 0 25px 0 0; + float: left; +} +blockquote:right { + margin: 0 0 0 25px; + float: right; +} +blockquote:before, +blockquote:after { + display: block; + content: ''; + width: 100%; + height: 4px; + /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#268aff+0,0b4fa0+100 */ + background: #268aff; /* Old browsers */ + background: -moz-linear-gradient(left, #268aff 0%, #0b4fa0 100%); /* FF3.6-15 */ + background: -webkit-linear-gradient(left, #268aff 0%,#0b4fa0 100%); /* Chrome10-25,Safari5.1-6 */ + background: linear-gradient(to right, #268aff 0%,#0b4fa0 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#268aff', endColorstr='#0b4fa0',GradientType=1 ); /* IE6-9 */ +} +blockquote:before { + margin: 0 0 15px 0; +} +blockquote:after { + margin: 15px 0 0 0; +} + +div.more { + margin: 15px 0; +} +.btn-white { + background: #fff; + color: #0b4fa0; + border: 1px solid #0b4fa0; + font-size: 19px; + padding: 3px 9px; +} +.btn-white:hover, +.btn-white:active, +.btn-white:focus { + background: #fff; + color: #006ae6; + border: 1px solid #006ae6; +} +.btn-more { + background: #fff; + border: 1px dotted #0b4fa0; + color: #006ae6; + font-size: 15px; + font-family: 'Roboto Slab', monospace; + font-weight: bold; + text-transform: capitalize; + box-sizing: border-box; + padding: 3px 10px; +} +.btn-more:hover, +.btn-more:active, +.btn-more:focus { + background: #006ae6; + color: #fff; + border: 1px solid #fff; +} + +/* FORM ELEMENTS */ +.input-group { + padding: 0 0 0 20px; + margin: 1.5em 0 1em 0; +} + +label { + color: #0b4fa0; + font-weight: 600; +} +.select { + color: #0b4fa0; +} +.text-input { + background: #e4f0ff; + font-size: 15px; + border: 0; + padding: 7.5px 15px; + color: #0b4fa0; + display: inline-block; + margin: 0 5px; +} + +input[type="radio"] + label span { + display:inline-block; + width: 15px; + height: 15px; + margin:-2px 20px 0 0; + vertical-align:middle; + cursor:pointer; + -moz-border-radius: 50%; + border-radius: 50%; +} + +input[type="radio"] + label span { + background: #fff; + box-shadow: 0 0 0 4px #e4f0ff; +} + +input[type="radio"]:checked + label span{ + background-color: #0b4fa0; + border: 2px solid #fff; +} + +input[type="radio"] + label span, +input[type="radio"]:checked + label span { + -webkit-transition:background-color 0.3s linear; + -o-transition:background-color 0.3s linear; + -moz-transition:background-color 0.3s linear; + transition:background-color 0.3s linear; +} + +.checkbox { + /* Double-sized Checkboxes */ + -ms-transform: scale(1.2); /* IE */ + -moz-transform: scale(1.2); /* FF */ + -webkit-transform: scale(1.2); /* Safari and Chrome */ + -o-transform: scale(1.2); /* Opera */ + padding: 5px; + font-size: 40px; + position: relative; + top: 2px; +} + +/* IMAGE TREATMENTS */ +.img-greyscale img { + -webkit-filter: grayscale(100%); + filter: gray; + filter: grayscale(100%); +} +.img-greyscale img:hover { + -webkit-filter: grayscale(0%); + filter: none; + filter: grayscale(0); +} + +.img-responsive { + /* + width: 100%; + height: auto; + FIXME + */ + display: block; + margin: 0 auto; + border: 0; +} + +.crop { + width: 193px; + height: 144px; + overflow: hidden; +} + +.crop img { + max-width: none !important; + width: 300px; + height: 166px; + margin: 0px 0 0 -50px !important; +} + +.crop-2 { + width: 300px; + height: 166px; + overflow: hidden; +} + +.crop-2 img { + max-width: none !important; + width: 470px; + margin-left: -90px; +} + +.crop-3 { + width: 470px; + height: 260px; + overflow: hidden; +} + +.crop-3 img { + max-width: none !important; + width: 470px; + height: 260px; +} + +.detail_text .img-responsive { + max-width: 625px; +} + +.home-featured { + margin-bottom: 25px; +} +.home-featured img { + max-width: 470px; +} + +.home-featured [class*='col-']:first-child { + padding-right: 0; + padding-left: 0; + max-width: 470px; +} + +.highlight-list [class*='col-4'] { + min-width: 193px; +} + +.highlight img, +.highlight h3, +.highlight .info { + max-width: 193px; + margin: 0 auto; +} + +.highlight h3 { + font-size: 18px; +} + +.swiper-slide img { + display: block; +} + +.swiper-holder .index, +.swiper-holder .slideshow-controller { + display: none; +} +/* Gallery */ + +.swiper-container { + width: 100%; + height: auto; + margin-left: auto; + margin-right: auto; +} + +.swiper-slide .caption { + display: block; + font-size: 14px; + width: 100%; + padding: 20px 135px 20px 0; + min-height: 60px; +} + +.swiper-content { + height: auto; + min-height: 300px; + width: 100%; + padding-bottom: 10px; + margin-bottom: 5px !important; + display: block; + margin-top: 10px !important; +} +.swiper-content .swiper-slide img { + display: block; + height: auto; + padding-bottom: 4px; + background: #268aff; + background: -moz-linear-gradient(left, #268aff 0%, #0b4fa0 100%); + background: -webkit-linear-gradient(left, #268aff 0%,#0b4fa0 100%); + background: linear-gradient(to right, #268aff 0%,#0b4fa0 100%); + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#268aff', endColorstr='#0b4fa0',GradientType=1 ); +} + +.slideshow-controller { + display: block; + position: relative; + width: auto; + right: 0; + padding: 15px 0 0 10px; + z-index: 500; + background: transparent; + text-align: right; + margin-top: -40px; + margin-bottom: 10px; + border-bottom: 1px dotted #4f4f4f; + min-height: 97px; +} + +.slideshow-controller .index { + margin-top: 20px; + padding-bottom: 20px; + font-size: 12px; +} +.swiper-nav { + position: relative; + height: auto; + box-sizing: border-box; + padding: 10px 0; + margin-bottom: 15px; + display: block; +} +.swiper-nav .swiper-slide img { + display: block; + margin-right: 10px; + width: 107px; + height: auto; +} +.swiper-nav .swiper-slide { + -moz-opacity: 0.40; + opacity:0.40; + filter: alpha(opacity=40); + cursor: pointer; +} +.swiper-nav .swiper-wrapper { + height: auto !important; +} + +.swiper-nav .swiper-slide-active { + -moz-opacity: 1; + opacity: 1; + filter: alpha(opacity=100); +} + +.swiper-button { + position: absolute; + display: block; + width: 85px; + height: 90px; + line-height: 62px; + color: #006ae6; + font-family: 'Oswald', sans-serif; + font-size: 40px; + font-weight: bold; + top: 0 !important; + margin-top: 9px; + color: #0b4fa0; + background: #fff; + text-align: center; + cursor: pointer; +} + +.swiper-button-next { + right: 0; +} +.swiper-button-prev { + left: 0; +} + +.swiper-nav .swiper-slide-active img { + border: 3px solid #006ae6; +} + +.divider-top, +.divider-bottom { + display: block; + clear: both; + width: 100%; + height: 20px; + background: #fff; + border-left: 1px dotted #a0a0a0; + border-right: 1px dotted #a0a0a0; + margin-top: 10px; + position: relative; + z-index: 100; +} + +.divider-top { + border-top: 1px dotted #a0a0a0; +} + +.divider-bottom { + border-bottom: 1px dotted #a0a0a0; + margin-top: 20px; + margin-bottom: 40px; +} + +.partners { + text-align: center; +} +.partners h3 { + font-size: 22px; + text-transform: uppercase; + margin-bottom: 20px; + letter-spacing: 2px; +} +.partners img { + display: inline; + margin-right: 20px; +} + +/* HELPER */ +.italic { + font-style: italic; +} +.center { + text-align: center; +} +.right { + text-align: right; +} +.float-left { + display: block; + float: left; +} +.float-right { + display: inline-block; + margin-left: 10px !important; + float: right !important; +} +.relative { + position: relative; +} +.absolute-top { + position: absolute; + width: 100%; + right: 20px; + top: 0; +} +.full-width { + width: 100%; + display: block; +} +.uppercase { + text-transform: uppercase; +} + +.justify { + text-align: justify; + -ms-text-justify: distribute-all-lines; + text-justify: distribute-all-lines; +} +.justify > div { + display: inline-block; + vertical-align: top; + + /* IE fix. */ + *display: inline; + zoom: 1; +} +.justify:after { + content: ""; + width: 100%; + display: inline-block; +} + +/* parent container would need to set height and line-height to the same X px */ +.vertical-align { + display: inline-block; + vertical-align: middle; + text-align: center; + line-height: 15px; +} + +/* ADVERTS */ +.advert-large { + width: 970px; + margin: 20px auto 20px auto; +} +.advert { + width: 100%; + overflow: hidden; + max-width: 300px; + display: block; + margin: 0 auto 15px auto; + background: #fff; +} +.advert.float-left { + margin-right: 20px !important; + margin-bottom: 20px !important; +} +.advert-small img { + width: 300px; + height: auto; + text-align: center; +} +.list-view.col-6-12 { + clear: none; +} +.list-view { + display: block; + clear: both; + margin-bottom: 20px; +} +.list-view:last-child { + margin-bottom: 0; +} +.list-view .thumb { + float: left; + width: 107px; + height: 80px; +} +.list-view .detail { + overflow:hidden; + float: none; + padding: 0 15px 0 15px +} + +.list-view h3 { + font-size: 13px; + font-family: 'Open Sans', sans-serif; + padding: 0px; +} +.list-view .label { + text-align: left; + padding: 0 10px 0 0; +} + +.cat-view { + margin-bottom: 20px; + border-bottom: 1px dotted #4f4f4f; +} +.cat-view .post-meta { + white-space: nowrap; +} +.cat-view:last-child { + border-bottom: 0; +} +.cat-view .label { + padding-left: 0; + border-bottom: 0; + font-size: 20px; +} +.cat-view li a { + color: initial; + padding-right: 5px; +} +.cat-view li span { + padding: 0 5px 0 0; +} +.cat-view .new { + display: inline-block; + padding: 0 4px 1px 4px; + color: #fff; + background: #006EDF; + font-size: 9px; + text-transform: uppercase; +} +.cat-view [class*="col-1-2"] { + padding-right: 0; +} + +.cat-view .detail { + padding-left: 20px; +} + +.icon-view { + padding-top: 20px; +} +.icon-view h3 { + font-weight: 500; + text-transform: uppercase; +} + +.icon-view .listing { + display: block; + background: #e4f0ff; + padding: 20px 0; + margin-bottom: 15px; + margin-right: 5%; + width: 47.5%; +} + +.icon-view .listing:nth-child(even) { + margin-right: 0; +} + +.icon-view .list { + display: table-cell; +} +.icon-view .icon { + display: table-cell; + float: left; + background: #fff; + border-radius: 50%; + width: 62px; + height: 63px; + padding: 6px 0 0 5px; + margin-right: 20px; + margin-left: 20px; +} +.icon-view [class*="sprite-"] { + display: block; + width: 52px; + height: 52px; +} + +.liststyle { + list-style: square; + margin: 0; + padding: 0 0 0 15px; +} + +.liststyle li:before { + display: inline-block; + padding-top: 2px; + margin-right: 8px; + margin-left: -14px; + color: #006ae6; /* whatever color you prefer */ + font-size: 20px;/* or whatever the bullet size you prefer */ + position: relative; + top: 2px; +} + + +.highlight-list h3 { + font-weight: 400; + padding-bottom: 0; + margin: 0 auto; +} + +.highlight { + padding-bottom: 25px; +} + +.related .highlight { + padding-bottom: 0; +} + +.list-block [class*='col-'] { + padding-right: 0; +} + +.list-block { + font-size: 15px; + line-height: 1.6; + border-bottom: 1px dotted #4f4f4f; + padding-bottom: 20px; + margin-bottom: 10px; +} + +.list-block:last-child, +.list-block.last { + border-bottom: 0; +} + +.details { + line-height: 1.6; +} +.details h2, .details h3 { + font-family: 'Open Sans', sans-serif; + color: #0b4fa0; + margin: 0; + font-weight: 600 !important; +} + +.details h2 { + font-size: 20px; +} + +.details h3 { + font-size: 15px; +} + +.detail-block { + display: block; + margin: 10px 0; +} +.detail-block .highlight-block { + display: block; + margin: 5px 0 0 0; + padding: 10px; + background: #f4f8fb; +} + +.detail_text.article img { + float: left; + margin-right: 10px; + margin-bottom: 20px; + margin-top: 20px; +} +/* ratings */ +.biz-ratings { + margin-top: 15px; +} +.rating { + width:80px; + height:16px; + margin:0px 0px 20px 0px; + padding:0; + list-style:none; + float: left; + position:relative; + background: url(images/star-matrix.gif) no-repeat 0 0; +} +ul.rating li { + cursor: pointer; + /*ie5 mac doesn't like it if the list is floated\*/ + float:left; + /* end hide*/ + text-indent:-999em; +} +ul.rating li a { + position:absolute; + left:0; + top:0; + width:16px; + height:16px; + text-decoration:none; + z-index: 200; +} +ul.rating li.one a {left:0} +ul.rating li.two a {left:16px;} +ul.rating li.three a {left:32px;} +ul.rating li.four a {left:48px;} +ul.rating li.five a {left:64px;} +ul.rating li a:hover { + z-index:2; + width:80px; + height:16px; + overflow:hidden; + left:0; + background: url(images/star-matrix.gif) no-repeat 0 0; +} +ul.rating li.one a:hover {background-position:0 -96px;} +ul.rating li.two a:hover {background-position:0 -112px;} +ul.rating li.three a:hover {background-position:0 -128px} +ul.rating li.four a:hover {background-position:0 -144px} +ul.rating li.five a:hover {background-position:0 -160px} + +/* this is used to remove the hover affect */ +/* use the background position according to the table above to display the required images*/ +.rated{ + width:80px; + height:16px; + margin: 4px 0px 16px 0px; + padding:0; + list-style:none; + float: left; + position:relative; + background: url(images/star-matrix.gif) no-repeat 0 0; +} +ul.rated li { + cursor: pointer; + /*ie5 mac doesn't like it if the list is floated\*/ + float:left; + /* end hide*/ + text-indent:-999em; +} +ul.rated li.one a {left:0} +ul.rated li.two a {left:16px;} +ul.rated li.three a {left:32px;} +ul.rated li.four a {left:48px;} +ul.rated li.five a {left:64px;} + +/* add these classes to the ul to effect the change to the correct number of stars */ +.nostar {background-position:0 0} +.onestar {background-position:0 -16px} +.twostar {background-position:0 -32px} +.threestar {background-position:0 -48px} +.fourstar {background-position:0 -64px} +.fivestar {background-position:0 -80px} +/* end rating code */ + +/* bio */ +.bio { + margin: 20px 0; +} + +.bio .img-responsive { + float: left; + margin-right: 10px; +} + +.bio p { + padding-top: 0; + margin-top: 0; + padding-bottom: 10px; +} + +.section { + padding-right: 0; +} +.section-has-widgets { + padding-right: 25px; +} +.section .sidebar { + padding-top: 0; +} +.section.featured { + margin-bottom: -20px; +} + +.section.social { + padding: 0 0 0 3%; + margin-right: 0; +} + +.sidebar-right { + float: left; + clear: right; +} + +.home .section h1 { + text-align: center; + margin-bottom: 0; + font-weight: 500; +} +.featured-hero h1 { + padding-bottom: 10px; +} +.listings .section h1 { + padding-bottom: 10px; +} +.section.highlight-list h1 { + margin: 0; +} + +.section.article h1 { + text-align: left; + font-size: 39px; + padding-top: 0; +} +.post-meta .author { + text-transform: uppercase; +} +.post-meta .author a { + color: #adadad; +} +.section.article .post-meta .time { + float: right; +} +.section.article .post-meta { + margin-top: 0; + padding: 0 20px; + height: auto; + line-height: 200%; + display: block; +} +.section.social .comment iframe { + width: 100% !important; +} +.share-buttons > div { + float: left; + display: block; + margin-right: 10px; + height: 80px; + overflow: hidden; +} +.section-spacer { + height: 20px; + display: block; + width: 100%; +} +.section-spacer-large { + height: 40px; + display: block; + width: 100%; +} +.section-divider { + /* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#268aff+0,0b4fa0+100 */ + background: #268aff; /* Old browsers */ + background: -moz-linear-gradient(left, #268aff 0%, #0b4fa0 100%); /* FF3.6-15 */ + background: -webkit-linear-gradient(left, #268aff 0%,#0b4fa0 100%); /* Chrome10-25,Safari5.1-6 */ + background: linear-gradient(to right, #268aff 0%,#0b4fa0 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */ + filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#268aff', endColorstr='#0b4fa0',GradientType=1 ); /* IE6-9 */ + width: 100%; + height: 4px; + margin: 25px 0; +} +.article-content { + position: relative; + font-size: 15px; + line-height: 1.6; + text-align: justify; +} + +.grid-container { + margin-left: -25px; +} + +#home .main, +.home .main { + margin-top: 0; /* reset margin top to 0 */ +} +.label { + font-weight: bold; + font-family: 'Roboto Slab', monospace; + border-bottom: 1px dotted #0b4fa0; + padding: 3px 15px; + text-decoration: none; + color: #006ae6; + font-size: 15px; +} +.label:hover, +.label:focus, +.label:active { + color: #0b4fa0; + border-bottom: 1px solid #0b4fa0; + text-decoration: none; +} + +.featured-hero { + text-align: center; + display: block; + padding: 14px 5% 0 5%; +} + +.featured-hero .label { + font-size: 15px; + margin-bottom: 10px; + display: inline-block; +} +.featured-hero .excerpt { + font-size: 15px; + font-weight: 300; + line-height: 1.6; + margin-bottom: 6px; +} + +.briefy { + text-align: left; + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical +} + +.briefy-b { + display: inline-block; + white-space: nowrap; + overflow: hidden; + max-width: 100%; + vertical-align: bottom; + text-overflow: ellipsis; +} + +.briefy p { + margin: 0; +} + +.post-meta { + position: relative; +} +.post-meta span { + display: inline-block; +} + +i.icon { + display: inline-block; + width: 23px; + height: 18px; + position: relative; + top: 4px; +} + +i.map-icon { + display: inline-block; + position: relative; + top: 4px; + width: 21px; + height: 25px; + background-size: 400px 294px; +} + +.bar { + display: inline-block; + width: 2px; + height: 9px; + background: #0b4fa0; + position: relative; + top: -2px; + margin: 0 1px; +} +.triangle-left { + display: inline-block; + width: 0; + height: 0; + border-style: solid; + border-width: 6px 6px 6px 0; + border-color: transparent #0b4fa0 transparent transparent; +} +.triangle-right { + display: inline-block; + width: 0; + height: 0; + border-style: solid; + border-width: 6px 0 6px 6px; + border-color: transparent transparent transparent #0b4fa0; +} +.sprite-slowtwitch { + background-position: -169px -200px; +} + +.sprite-gallery { + display: inline-block; + width: 23px; + height: 13px; + position: relative; + top: 1px; + margin-left: 6px; + background-position: -180px -370px; +} +.video-thumb { + display: block; + position: relative; +} +.sprite-video { + display: inline-block; + width: 23px; + height: 18px; + position: relative; + top: 4px; + margin-left: 10px; + background-position: -149px -370px; +} +.list-view .sprite-video { + top: 8px; +} +.sprite-video-play { + background-position: -138px -300px; + display: block; + height: 70px; + width: 70px; + position: absolute; + top: 50%; + left: 50%; + margin-top: -35px; + margin-left: -35px; +} +/* for phrase II where tagging for sports are available: +How to use: add the tag right below the : + */ +.sprite-sports-small { + display: block; + position: absolute; + height: 25px; + width: 31px; + right: 0; + bottom: 0; +} +.sprite-icon-small { + display: block; + height: 25px; + width: 25px; +} +.sprite-running-small { + background-position: -69px -400px; +} +.sprite-running { + background-position: -387px -288px; +} +.sprite-biking { + background-position: -314px -296px; +} +.sprite-race { + background-position: -590px -298px; +} +.sprite-map { + background-position: -243px -300px; +} +.sprite-roadshow { + background-position: -129px -405px; +} +.sprite-coaches { + background-position: -595px -365px; +} +.sprite-clubs { + background-position: -528px -291px; +} +.sprite-fitters { + background-position: -456px -292px; +} +.sprite-stores { + background-position: -527px -363px; +} +.sprite-bike { + background-position: -250px -358px; +} +.sprite-stem { + background-position: -316px -362px; +} +.sprite-stack { + background-position: -386px -363px; +} +.sprite-bar { + background-position: -449px -370px; +} +.map-icon.sprite-race { + background-position: 0px -258px; +} +.map-icon.sprite-clubs { + background-position: -22px -258px; +} +.map-icon.sprite-coaches { + background-position: -45px -258px; +} +.map-icon.sprite-running { + background-position: -66px -258px; +} +.map-icon.sprite-biking { + background-position: -89px -258px; +} +.map-icon.sprite-fitter { + background-position: -112px -258px; +} +.map-icon.sprite-roadshow { + background-position: -79px -230px; +} +.featured-content { + text-align: center; + margin-bottom: 25px; +} + +.featured-wrapper .featured-content { + padding-right: 10px; +} + +.featured-wrapper .featured-content:nth-child(2), +.featured-wrapper .featured-content:last-child { + padding-right: 0; + padding-left: 10px; +} + +.featured-content img { + margin-bottom: 10px; +} + +.featured-content .label, +.detail.cat-content .label { + font-size: 15px; +} + +.detail.cat-content h3, +.featured-content h3 { + margin-top: 5px; + font-weight: 500; + font-size: 18px; +} + +.label-category { + letter-spacing: 1px; +} + +.subcategory { + padding: 0 25px; +} + +.subcategory h3, +h3.section-title { + display: inline-block; + font-weight: normal; + font-size: 14px; + text-transform: uppercase; + border-bottom: 1px dotted #ccc; + margin-bottom: 5px; +} + +h3.section-title { + margin-bottom: 10px; +} + +.subcategory dl { + float: left; + width: 50%; + list-style: none; + margin: 0; + padding: 0; +} +.subcategory a { + padding: 7px 0; + font-weight: bold; +} + +.widget.subcategory label { + float: left; + color: #000; + padding-top: 8px; + padding-right: 8px; +} + +.widget.subcategory select { + float: left; + width: 185px; +} + +/* SIDEBARS and WIDGETS */ +.sidebar-b { + float: right; + overflow: hidden; + width: 300px; +} + +.widget { + padding: 0 25px; +} + +.widget.nodivider { + margin-bottom: 40px; +} +.widget-spacer { + display: block; + height: 25px; +} +.widget .label { + margin-bottom: 15px; +} +.widget form .btn, +.widget-form .btn { + min-width: 80px; +} +.widget-fullwidth { + padding: 0; +} + +.widget img { + max-width: 300px; +} +.widget .excerpt { + margin: 10px 0; +} +.widget .product-image { + margin: 20px auto 10px auto; +} +.widget .product { + font-size: 13px; +} + +.widget .product .post-meta, +.widget .product .excerpt { + padding: 0 20px; +} + +.widget .product .excerpt { + display: block; + margin: 15px 0; +} + +.widget h2 { + text-align: center; + font-size: 24px; + text-transform: uppercase; + letter-spacing: 2px; + margin: 0 0 5px 0; +} + +.widget h3 { + text-align: center; + font-size: 22px; + color: #0b4fa0; + padding-top: 0; + padding-bottom: 5px; + margin-bottom: 5px; + border-bottom: 1px solid #adadad; + font-weight: 500; +} + +.widget.poll .question { + font-size: 13px; + padding: 0 0 15px 0; +} + +.widget .description { + font-size: 13px; + text-align: center; + padding: 0 0 15px 0; +} +.content-divider-top, +.widget-divider { + display: block; + width: 100%; + height: 20px; + border-left: 1px dotted #4f4f4f; + border-bottom: 1px dotted #4f4f4f; +} +.content-divider-top { + margin-bottom: 15px; +} +.widget-divider { + margin-bottom: 40px; +} +.content-divider-bottom { + display: block; + width: 100%; + height: 20px; + border-left: 1px dotted #4f4f4f; + border-top: 1px dotted #4f4f4f; + margin-top: 40px; + margin-bottom: 0; +} + +.widget-form .row .btn { + position: relative; + z-index: 100; + width: 38%; + text-align: center; + float: none; + margin-left: 1%; + padding: 6px 0; +} + +.widget-form .row .txt { + overflow: hidden; + float: left; + height: 31px; + width: 58%; +} + +.widget.has-border { + padding-right: 0; +} + +.widget.has-border .list { + border-right: 1px dotted #4f4f4f; + padding-right: 10px; +} +.widget .list .link { + display: block; + width: 100%; + padding: 7px 3px 7px 0; +} +.widget .list .link a { + display: table-cell; + padding-left: 10px; + color: #272727; +} + +.widget .list .link div { + display: table-cell; + position: relative; + top: 3px; +} +.widget .list span { + display: block; + text-align: center; + font-size: 12px; + width: 29px; + background: #0b4fa0; + color: #fff; + font-weight: 500; + font-family: 'Oswald', sans-serif; +} +.widget .list.dated span { + width: 46px; + text-transform: uppercase; + font-weight: 400; +} + + +.widget .listings { + font-size: 13px; + text-transform: uppercase; + font-family: Oswald; + width: 100%; + display: block; + text-align: center; +} + +.widget .listings > div { + display: inline-block; + text-align: center; + position: relative; + max-width: 20%; +} + +.widget .listings .icon-divider { + margin: 0 1.5%; +} + +.widget .listings a > div { + height: 52px; + line-height: 52px; + text-align: center; +} + +.widget .listings [class*="sprite-"] { + display: block; + width: 52px; + height: 52px; + margin: 10px auto 3px auto; +} + +.icon-divider { + width: 0; + height: 30px; + border-right: 1px dotted #332F2F; + position: relative; + top: 20px; + margin-right: -1px; +} +.icon-spacer { + width: 1px; + height: 30px; + position: relative; + top: 20px; + margin-right: -1px; +} + +.widget .region-list { + font-size: 14px; + font-weight: bold; +} +.widget .region-list .list-item span { + background: #e4f0ff; + display: inline-block; + text-align: center; + font-size: 16px; + width: 16px; + line-height: 14px; + color: #0b4fa0; + cursor: pointer; + font-family: 'Arial', sans-serif; +} +.widget .region-list .list-item { + display: block; + width: 100%; + margin: 10px 0; +} +.widget .region-list .list-item a { + display: inline-block; + min-width: 180px; + margin: 5px 0px 5px 0; +} + +.widget .region-list .sublist { + display: none; +} + +.widget .region-list .sublist a { + display: block; + color: #000; + width: 100%; + margin: 3px 0; + padding-left: 10px; + font-weight: 600; +} + +.widget .region-list .top-ranked, +.widget .region-list .advsearch { + line-height: 26px; + text-transform: uppercase; +} +.widget .region-list .top-ranked .star { + color: #FFB300; + font-size: 22px; + position: relative; + top: 1px; +} + +.widget.coaching { + margin-top: 10px; + text-align: center; +} + +.dtable.addlink .dbody .dcell { + border: 0; +} +/* POLL OERRIDE */ +.poll .result-row { + padding: 0 0 10px 0; +} +.poll .meter { + display: table-cell; + width: 80px; +} +.poll .meter span { + position: relative; + display: block; + top: 2px; + left: 0; + width: 100%; + height: 13px; + background: #e4f0ff; + border: 0 solid #0b4fa0; +} +.poll .total-votes { + display: block; + text-align: center; + width: 100%; + margin: 5px 0 15px 0; + font-weight: bold; +} +.poll .percentage { + display: table-cell; + width: 35px; + color: #0b4fa0; + font-weight: bold; +} + +.poll .choice { + display: table-cell; + padding: 0 0 10px 5px; + position: relative; + top: -1px; +} +.poll .input-group.choice { + display: block; + padding: 0 0 5px 5px; +} + + +/* MAP */ +.alert { + display: block; + background: #e4f0ff; + padding: 15px; + margin-bottom: 10px; + color: #0b4fa0; + border-radius: 0px; +} +.alert .location { + font-weight: 600; +} +.map-control { + display: block; + margin: 10px 0 20px 0; + font-size: 15px; +} +.map-control .select, +.map-control .text-input { + margin-bottom: 10px; +} +.map-control .select { + margin-left: 10px; +} +.map-container { + position: relative; +} +.map { + width: 100%; + height: 500px; + margin-bottom: 20px; +} +.map-single { + width: 100%; + height: 500px; + margin-bottom: 20px; + border: 1px solid #333; + margin: 10px 0; +} +.map-large { + height: 600px; +} +.map-category * { + cursor: pointer; +} +.map-category { + position: absolute; + top: 50px; + right: 7px; + display: block; + width: 152px; + background: #fff; + font-size: 11px; + font-weight: bold; + letter-spacing: 1px; +} +.map-category label { + color: #000; +} +.map-category .category { + line-height: 35px; + padding: 4px; + margin: 0 5px; + border-bottom: 1px dotted #0b4fa0; +} +.map-category .category.last { + border-bottom: 0; +} +.map-category i { + float: right; +} +.map-category .hide-button { + background: #000; + color: #fff; + text-align: center; + font-weight: bold; + text-transform: uppercase; + padding: 6px 0; +} +.gm-style-iw { + width: 280px !important; +} +.gm-style-iw h4 { + margin: 0 0 3px 0; + color: #0b4fa0; + font-size: 15px; +} +.gm-style-iw { + line-height: 1.6; +} + +.dtable.form .value .col { + display: inline-block; + width: 32%; +} + +.dtable.form .value table { + margin-bottom: 10px; +} + +.dtable.form .value td { + border: 0; +} + +.note { + font-size: 13px; + font-weight: bold; + margin-top: 10px; +} + +.btn.disabled, +.btn.disabled:hover { + background: #ccc !important; + cursor: default !important; +} +.btn.light { + font-weight: 300; +} +.shop-btns .btn { + font-size: 12px; + padding: 5px 3px; + margin-top: 10px; +} + +.region-select { + display: block; + margin: 0 auto; + width: 95%; +} + +.actions { + text-align: center; + margin: 10px 0; + border-top: 1px solid #ccc; +} +.actions a { + padding: 0 5px; +} + +.search-results h1 { + margin-bottom: 20px; + padding-bottom: 0; +} + +.search-results ul { + margin: 0px; + padding: 0 19px; + color: #0b4fa0; +} + +.search-results .toolbar-b h3 { + overflow: hidden; + display: inline-block; +} +.search-results .toolbar-b .paging { + margin-top: 10px; +} + +.search-results .searchhl-1 { + color: #268aff; +} + +.search-results .searchhl-2 { + color: #aaaaff; +} + +.lnkactions { + margin: 10px 0; + font-weight: bold; +} + +.lnkactions a { + display: inline-block; + margin-right: 10px; + text-decoration: underline; +} +.lnkactions a:hover { + text-decoration: none; +} +.btn-wrapper { + padding: 20px 0; + text-align: center; +} +.appform { + display: none; +} +.appform iframe { + width: 100%; + height: 100%; + border: 0; +} +/* RESPONSIVE */ +/* tablet portrait and landscape */ +@media (max-width: 990px) { +/* portrait */ + .main { + margin-left: 12px; + margin-right: 12px; + } + + .header .mashead-b { + height: 115px; + } + + .content { + padding-right: 20px; + } + + .article .content, + .section-static .content { + padding-right: 0px; + } + + .section-has-widgets { + padding-right: 0px; + } + + .section { + padding: 0; + } + + .home-featured .col-1-2 { + width: auto; + padding-left: 0; + padding-right: 0; + } + + .home-featured .col-1-2:first-child { + float: left; + display: inline-block; + max-width: 390px; + } + + .home-featured .col-1-2:last-child { + float: none; + overflow: hidden; + padding-top: 5px; + } + .home-featured .img-responsive { + width: 100%; + max-height: 234px; + } + .featured-hero h1 { + font-size: 28px; + } + .featured-hero .excerpt { + font-size: 13px; + } + .list-view .detail { + padding-right: 0px; + } + .featured-content { + width: 50%; + } + .remove-sidebar .sidebar-b { + display: none; + } + + .remove-sidebar .content { + float: none; + width: auto; + margin: 0; + padding: 0; + } + + .article .sidebar-b, + .article .content, + .category-list.section-reverse .sidebar-b, + .category-list.section-reverse .content, + .section-static.section-reverse .sidebar-b, + .section-static.section-reverse .content { + float: none; + width: auto !important; + margin: 0; + } + + .featured-photo-video.section-reverse .content, + .featured-photo-video.section-reverse .sidebar-b { + float: none; + width: auto; + display: block; + overflow: visible; + text-align: center; + margin: 0 auto; + } + + .section-static.section-reverse, + .section-static.section-reverse .content, + .section-static.section-reverse .sidebar-b, + .featured-photo-video.section-reverse, + .featured-photo-video.section-reverse .content, + .featured-photo-video.section-reverse .sidebar-b, + .article.section-reverse, + .article.section-reverse .content, + .article.section-reverse .sidebar-b, + .category-list.section-reverse, + .category-list.section-reverse .content, + .category-list.section-reverse .sidebar-b { + transform: scaleY(-1); + -ms-transform: scaleY(-1); + -webkit-transform: scaleY(-1); + } + + .category-list.section-reverse .section-divider.mobile, + .section-static.section-reverse .section-divider.mobile { + display: block; + } + + .latest-headlines [class*='col-6'] { + width: 100%; + } + + .latest-headlines .advert { + display: none; + } + + .highlight-list [class*='col-4'] { + padding-right: 0; + padding-left: 0; + margin-right: 2%; + text-align: center; + width: 48%; + max-width: 300px; + } + .highlight-list h3 { + font-size: 16px; + } + + .related.highlight-list [class*='col-4'] { + margin-right: 0; + width: 33.33%; + max-width: none; + } + + .show-3 .list-view:nth-child(n+5) { + display: none; + } + + .show-6 .list-view:nth-child(n+8) { + display: none; + } + + .widget { + padding: 0 10px; + } + + .tablet { + display: block; + } + + .swiper-nav { + visibility: hidden; + height: 0px; + } + + .swiper-slide { + text-align: center; + } + + .swiper-slide .caption { + padding: 20px 0 0 0; + text-align: left; + } + + .swiper-holder { + position: relative; + display: inline-block; + margin: 0 auto; + } + + .swiper-holder .index { + display: inline; + position: absolute; + bottom: 8px; + right: 10px; + color: #fff; + padding: 0 5px; + z-index: 500; + } + + .slideshow-controller { + display: none; + } + + .slideshow-controller.mobile { + position: absolute; + display: block; + width: 100%; + top: 43%; + left: 0; + padding: 0; + margin: 0 !important; + height: auto !important; + min-height: 0; + border: 0; + } + + .slideshow-controller.mobile .btn:hover { + background: #fff; + color: #0b4fa0; + } + + .slideshow-controller.mobile .btn { + float: right; + background: none; + color: #fff; + font-family: 'Oswald', sans-serif; + font-size: 35px; + line-height: 22px; + height: 40px; + width: 25px; + padding: 5px 0; + text-align: center; + margin: 0; + vertical-align: middle; + } + + .slideshow-controller.mobile .btn.btn-prev { + float: left; + } + + .swiper-content .swiper-slide img { + padding-bottom: 40px; + } + + .content-divider-bottom { + margin-top: 20px; + } + + .advert { + margin-bottom: 0; + } + + .crop-3 .img-responsive { + width: 80%; + height: auto; + margin: 0; + } +} + +@media (min-width: 991px) and (max-width: 1024px){ +/* landscape */ + .main { + margin-left: 12px; + margin-right: 12px; + } + + .section-has-widgets { + padding-right: 0px; + } + + .featured-content { + max-width: 300px; + padding: 0; + } + + .grid { + margin-right: 0; + } +} + +@media (max-width: 969px) { + .advert-large { + display: none; + } + + .detail_text .img-responsive { + max-width: 910px; + width: 100%; + height: auto; + } +} + +@media (max-width: 767px) { + + .section h1 { + padding-bottom: 20px; + } + + .content { + margin-left: 0; + margin-right: 0; + padding-right: 0; + } + + .footer .navbar { + height: 45px !important; + } + + .rating-table > div { + width: 45%; + } + .rating-table > div:nth-child(even) { + display: none; + } + .advert.float-left, + .advert.float-right { + float: none; + text-align: center; + padding: 0; + margin: 20px auto; + max-width: none; + } + .cat-view .more { + margin: 12px 0 14px 0; + } + .cat-view .detail { + padding-left: 0; + } + .cat-view .label { + text-align: center; + margin: 9px auto 0 auto; + padding: 3px 15px; + display: block; + } + + .detail.cat-content h3, + .featured-content h3 { + margin-top: 2px; + } + + .crop-3, + .crop-2, + .crop { + overflow: visible; + width: auto; + height: auto; + } + + .crop-2 img { + max-width: 675px !important; + width: 100%; + height: auto; + margin: 0px; + } + + .crop-3 .img-responsive { + max-width: 675px !important; + width: 100%; + height: auto; + } +} + +@media (max-width: 695px) { + .main { + margin: 0; + } + + .header .grphead { + margin-bottom: 20px; + } + + /* image and vid responsive */ + .img-responsive { + max-width: 675px; + width: 100%; + height: auto; + } + + .detail_text iframe, + .detail_text object, + .detail_text object embed { + width: 100% !important; + } + + .list-column:nth-child(1) { + margin-bottom: 30px; + } + + .home-featured .img-responsive { + width: auto; + max-height: none; + } + + .breadcrumb { + margin-bottom: 20px; + } + + .section, + .section.featured.home .content-has-sidebar { + margin: 0 10px; + } + + .section.featured.home { + margin: 2px 0 0 0; + } + + .featured-hero { + margin: 0 10px 10px 10px; + padding: 0; + } + + .featured-content h3 { + padding: 2px 0; + } + .featured-content img { + margin-bottom: 0; + } + + .featured-content .advert { + margin: 10px auto; + } + + [class*="col"], + .home-featured .col-1-2:first-child, + .sidebar-b, + .content { + float: none; + width: auto !important; + margin: 0; + } + + [class*="col"] { + max-width: none !important; + padding-right: 0 !important; + margin-right: 0 !important; + } + + .highlight:before, + .highlight:after, + .featured-content:before, + .featured-content:after { + display: table; + content: " "; + } + + .highlight:after, + .featured-content:after { + clear: both; + } + + + .home-featured { +/* border-bottom: 1px solid #adadad; */ + } + + .home-featured .col-1-2:last-child { + overflow: visible; + } + + .featured-wrapper .featured-content { + padding-left: 0 !important; + padding-right: 0 !important; + text-align: center; + + } + + .featured-wrapper .featured-content .detailed-url { + display: block; + padding: 0; + margin: 0 auto 10px auto; + text-align: center; + } + + .list-view { + margin-bottom: 20px; + } + + .list-view .list-category { + padding-bottom: 5px; + } + + .list-view .detail { + padding-left: 10px; + } + + .list-view .label { + display: inline-block; + padding: 3px 0px 3px 3px; + margin-bottom: 0; + } + + .list-view h3 { + font-size: 16px; + } + + .featured-wrapper .featured-content .excerpt { + clear: both; + padding-top: 5px; + margin-bottom: 20px; + } + + .archive .pagination { + text-align: center; + } + + .list-view .list-category { + margin-top: -3px; + } + + .list-view.withcategory { + margin-bottom: 25px; + } + + .list-view.withcategory .thumb { + margin-top: 0; + } + + .list-view.withcategory h3 { + margin-top: 0px; + } + + .list-view.withcategory .label { + padding-left: 0; + } + + .section-reverse, + .section-reverse .content, + .section-reverse .sidebar-b { + transform: scaleY(-1); + -ms-transform: scaleY(-1); + -webkit-transform: scaleY(-1); + } + + .section.section-reverse { + margin: 0; + } + + .section.section-reverse .content > .grid, + .section.section-reverse .sidebar-b { + padding: 0 12px; + } + + .related .highlight { + margin-bottom: 10px; + clear: both; + width: 100%; + } + + .highlight img { + float: right; + display: inline-block; + width: 50%; + height: auto; + } + + .highlight h3 { + max-width: none; + width: 70%; + text-align: left; + overflow: hidden; + display: block; + margin-left: 0; + } + + .highlight .info { + overflow: hidden; + display: block; + text-align: left; + max-width: none; + } + + .featured-photo-video .featured-content { + float: none; + width: 100%; + margin-bottom: 15px; + } + + .featured-photo-video .excerpt { + display: none; + } + + .widget.has-border .list { + border: 0; + } + + .col-1-1 .btn { + margin-top: 5px; + } + + .btn.btn-white.float-right { + margin-bottom: 5px; + } + + .widget-divider { + margin-bottom: 28px; + } + + .widget-column .more { + margin: 8px 0 35px 0; + } + + .listings .section h1 { + font-size: 34px; + } + + + .featured-category .label-category { + margin-bottom: 4px; + display: inline-block; + } + + .crop-3, + .crop-2, + .crop { + overflow: visible; + width: auto; + height: auto; + } + + .crop-2 img { + max-width: 675px !important; + width: 100%; + height: auto; + margin: 0px; + } + + .crop-3 .img-responsive { + max-width: 675px !important; + width: 100%; + height: auto; + } + + .appform-btn { + display: none; + } + .appform { + display: inline-block; + width: 100%; + height: 600px; + } +} diff --git a/site/common/static/css/swiper.min.css b/site/common/static/css/swiper.min.css new file mode 100644 index 0000000..aafd4a1 --- /dev/null +++ b/site/common/static/css/swiper.min.css @@ -0,0 +1,125 @@ +/* + * Swiper 2.7.6 + * Mobile touch slider and framework with hardware accelerated transitions + * + * http://www.idangero.us/sliders/swiper/ + * + * Copyright 2010-2015, Vladimir Kharlampidi + * The iDangero.us + * http://www.idangero.us/ + * + * Licensed under GPL & MIT + * + * Released on: February 11, 2015 +*/ +/* =============================================================== +Basic Swiper Styles +================================================================*/ +.swiper-container { + margin:0 auto; + position:relative; + overflow:hidden; + direction:ltr; + -webkit-backface-visibility:hidden; + -moz-backface-visibility:hidden; + -ms-backface-visibility:hidden; + -o-backface-visibility:hidden; + backface-visibility:hidden; + /* Fix of Webkit flickering */ + z-index:1; +} +.swiper-wrapper { + position:relative; + width:100%; + -webkit-transition-property:-webkit-transform, left, top; + -webkit-transition-duration:0s; + -webkit-transform:translate3d(0px,0,0); + -webkit-transition-timing-function:ease; + + -moz-transition-property:-moz-transform, left, top; + -moz-transition-duration:0s; + -moz-transform:translate3d(0px,0,0); + -moz-transition-timing-function:ease; + + -o-transition-property:-o-transform, left, top; + -o-transition-duration:0s; + -o-transform:translate3d(0px,0,0); + -o-transition-timing-function:ease; + -o-transform:translate(0px,0px); + + -ms-transition-property:-ms-transform, left, top; + -ms-transition-duration:0s; + -ms-transform:translate3d(0px,0,0); + -ms-transition-timing-function:ease; + + transition-property:transform, left, top; + transition-duration:0s; + transform:translate3d(0px,0,0); + transition-timing-function:ease; + + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} +.swiper-free-mode > .swiper-wrapper { + -webkit-transition-timing-function: ease-out; + -moz-transition-timing-function: ease-out; + -ms-transition-timing-function: ease-out; + -o-transition-timing-function: ease-out; + transition-timing-function: ease-out; + margin: 0 auto; +} +.swiper-slide { + float: left; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +/* IE10 Windows Phone 8 Fixes */ +.swiper-wp8-horizontal { + -ms-touch-action: pan-y; +} +.swiper-wp8-vertical { + -ms-touch-action: pan-x; +} + +/* =============================================================== +Your custom styles, here you need to specify container's and slide's +sizes, pagination, etc. +================================================================*/ +.swiper-container { + /* Specify Swiper's Size: */ + + /*width:200px; + height: 100px;*/ +} +.swiper-slide { + /* Specify Slides's Size: */ + + /*width: 100%; + height: 100%;*/ +} +.swiper-slide-active { + /* Specific active slide styling: */ + +} +.swiper-slide-visible { + /* Specific visible slide styling: */ + +} +/* =============================================================== +Pagination Styles +================================================================*/ +.swiper-pagination-switch { + /* Stylize pagination button: */ + +} +.swiper-active-switch { + /* Specific active button style: */ + +} +.swiper-visible-switch { + /* Specific visible button style: */ + +} diff --git a/site/common/templates/aboutus.html b/site/common/templates/aboutus.html new file mode 100644 index 0000000..98ec2b3 --- /dev/null +++ b/site/common/templates/aboutus.html @@ -0,0 +1,167 @@ + + + + <%site_title || 'slowtwitch.com'%>: About Us +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> + +
+ <%include include_header.html%> + +
+
+ <%~set title_loop = Links::Build::build('title', 'About Us')%> + <%include include_breadcrumb.html%> +
+ + +
<%-- start content--%> +
+

About Us

+
+
+
+ +

Dan Empfield - Slowtwitch Publisher: Born in California in 1957, Empfield is a graduate of South Tahoe High School and the University of Nevada (Reno), earning a Bachelor of Science in biology.

+

Empfield competed in his first triathlon in 1978, and in the first Ironman held on Hawaii's Big Island, in 1981.

+

He designed the original triathlon wetsuit, and founded wetsuit manufacturer Quintana Roo in 1987.

+

In 1989 Empfield debuted another design, the first bike "built from the aerobars back." The first Quintana Roo Superform had 650c wheels front and back, and an 80-degree seat angle. Today's modern triathlon and time trial bikes are progeny of the Superform.

+

He sold Quintana Roo to Saucony, Inc., in 1995, and stayed on to run Saucony's bike division - which included Quintana Roo and Merlin - for four years. He left in 1999 and founded Slowtwitch.com.

+

In 2003 Empfield developed the F.I.S.T. Tri Bike Fit System for triathletes, featuring "stack" and "reach," a set of bike fit metrics now popular among tri and road bike manufacturers and fitters. He's given fit clinics and workshops all over the United States and Europe. Today many modern fit systems employ the protocol, mechanics and metrics that are the backbone of the F.I.S.T. fit system.

+

Empfield was inducted into the Triathlete Magazine Hall of Fame in 2004, and received the World Open Water Swimming Association's (WOWSA) lifetime achievement award in 2010.

+

Empfield is a former USA Triathlon board member and, along with Lew Kidder, helped craft a set of USAT bylaws that reformed voting procedures, and redrew USAT voting regions. Most of those bylaws and districts remain in force today.

+

Empfield hosted the U.S. Professional National Triathlon Championship in 1998, held in accordance with Olympic draft-legal rules. This race was part of the first national series devoted to Olympic-rules triathlon - a series Empfield produced and funded.

+

Empfield continues to write for, and publish, Slowtwitch.com. He still swims, bikes, runs and races several times a year. He lives with wife Tanya, dogs, and horses on 8 acres in the San Gabriel Mountains of Southern California.

+
+
+ +

Herbert Krabel - Slowtwitch Editor in Chief: Born and raised in Southern Germany, Krabel graduated from Temple University in Philadelphia with a Bachelor of Arts degree in Communication in 1991.

+

While working full time at WYBE-TV 35 in Philadelphia as an editor, Krabel raced Mountain Bikes in the Pro category from 1991 through 1996. In 1997 he accepted a position as the Director of Marketing for Litespeed Titanium, the company he had raced for professionally. After the acquisition of the brands Quintana Roo and Merlin in 1999, the company became the American Bicycle Group and Krabel took over world wide marketing for all the brands.

+

In the spring of 2007 Krabel left the American Bicycle Group to start his non traditional marketing firm called Guerrilla Communication. That summer he also accepted the Editor in Chief position for Slowtwitch.com. He currently holds both these positions.

+

Krabel's athletic experiences include various Ironman finishes, the 24 Hours of Canaan in West Virginia, Wilderness 101 and Punxsutawney 50 in Pennsylvania and the Iditabike in Alaska. He lives with his wife Amy Hildreth and Silvie the feisty cat in Winston-Salem, NC.

+
+ +
+ +

Jordan Rapp - Slowtwitch Chief Technology Officer: After spending four years studying mechanical and aerospace engineering at Princeton University, Jordan Rapp only really came to enjoy it as a result of triathlon. A novice in all three sports, the idea of using equipment to go faster seemed like a great idea to a college rower used to racing for six minutes. 2012 was his 10th season of racing, and 7th as a pro, though he's enjoying a second crack at this whole triathlon thing and at life after being nearly killed in a hit-and-run in March of 2010 while riding his bicycle.

+

He earned his rainbow stripes by winning the ITU Long Distance World Championships in 2011. He's a five-time Ironman winner (US Championships in New York '12, Texas '12, Canada '09 & '11, and Arizona '09). And his first trip to the big dance in Kona in 2012 saw him finishing lucky number 13.

+

As CTO, Jordan is in charge of wrangling the multi-headed hydra of a website that is Slowtwitch.com. Despite being a somewhat Sisyphean ordeal, he nevertheless does his best to make sure Slowtwitch walks and talks the way it is supposed to. If you like Slowtwitch, feel free to let him know. If you don't, please tell Dan instead. Armed with his trusty Macbook Air, he tries to stay one step ahead of his ever growing “To Do” list.

+

More than a bit of a technophile, Jordan does the occasional product review for the site. Nothing makes him happier than cardboard boxes, with the possible exception of swim goggles, so this is the opiate that offsets the occasionally overwhelming job of coding the site.

+

Jordan also co-instructed the F.I.S.T. bike fit workshops with Dan for several years, where they helped people find enlightenment aboard a bicycle, before taking a leave of absence after the birth of his son. In addition to F.I.S.T.ing people, you can regularly find him around Xantusia where his favorite activity is climbing the mountain to Wrightwood, which is just about the only time Dan lets him away from his laptop.

+

Jordan is married to Canadian Olympian Jill Savege (Athens '04), winner of three ITU World Cups, and the holder of the best Kona debut in the house with a 10th place in 2001. They have one child - Quentin Thomas - born in 2011.

+
+
+ +

Greg Kopecky - Slowtwitch Editor-At-Large: A true Midwesterner, Kopecky was born and raised in central Indiana. He graduated with a degree in Organizational Leadership and Supervision from Purdue University, in West Lafayette. In addition to his formal education, Kopecky minored in reading Slowtwitch.com and riding his bike through the cornfields.

+

His sporting life began at a young age, joining the local club swim team at age seven. Over time, he took to distance running and weight training. Rather than pursue swimming as a college sport, Kopecky chose to join the university's student-managed Triathlon Club. This fostered a huge interest in the sport (in particular, the mechanical workings of bicycles), and led to the meeting of his now-wife at Collegiate Nationals in Reno, NV.

+

During college, Kopecky spent his summers working in a bike shop, and expanded his knowledge with book purchases and free education on the internet. After college, he worked for Zipp and SRAM, doing sports marketing and athlete support for "skinny tire - cycling disciplines" triathlon, road, cyclocross, and track. He took up his lifelong passion for writing in April of 2012, as the Tech Editor for Slowtwitch.com and LAVA Magazine (thanks to an endorsement from his friend, Jordan Rapp). After two years and many, many tech articles, Kopecky stepped down from the Tech Editor position, and took on a career with ISM Saddles as Brand Commander. He remains as an Editor-at-Large and occasional contributor to Slowtwitch when the creative itch finds him.

+
+
+ +

Timothy Carlson - Slowtwitch Senior Correspondent and Photographer: Born in Daytona Beach, Florida, Carlson is a graduate of Seabreeze Senior High where he set a school record for most touchdown passes caught (and dropped) and ran a leg on the state champion 880 relay in which he did not drop the baton. He also cleared 8 feet 6 inches in the pole vault, landing six feet past the pit, tumbled into a beautiful girl and subsequently asked her out. At Harvard College, he punted for the freshman football squad until he sailed a kick + into a stuff wind which came backward, forcing him to down the ball before + it settled in the Crimson's end zone. In his sophomore year, he took + pictures for the Harvard Crimson daily newspaper and was on assignment + inside University Hall during a student occupation when he was arrested with + the demonstrators by Cambridge police. After successfully smuggling his + pictures out of jail, several of his photos appeared in Life Magazine + coverage of the event.

+

In an eclectic post graduate period, he taught expository writing and worked + as a freelance writer and photographer during which he covered a wide range + of events including the Super Bowl, the Democratic and Republican + conventions, a coal miner's strike in West Virginia, the Daytona 500, NBA + finals and Palestinian refugees in Lebanon. In his most exciting assignment, + he served as a co-driver for the Polish Racing Drivers of America Ford + Econoline van in the original Cannonball Sea-to-Shining-Sea Memorial Trophy + Dash race from New York to Redondo Beach, California. With a makeshift + 120-gallon fuel tank, no one smoked and the first gas stop was in Missouri.

+

Finally gaining regular employment, he worked as a staff writer at the Los + Angeles Herald Examiner for 10 years, during which he wrote for the Style, + Sports, News, Magazine and editorial sections. During one feature + assignment, he rode as passenger at Riverside raceway in an off road truck + which flipped end over end. In another assignment to cover a murder in south + central LA, he was abducted and held overnight by a crack addict. At the + Herald's demise, he went to work for the LA Bureau of TV Guide magazine. + During that four year stint, he wrapped up a 10-year career racing off road + motorcycles (which included one spectacular crash which required he be + airlifted from Erendira to Scripps Hospital in San Diego)co-riding to a 5th + place finish in the Open Motorcycle division in the 1992 Baja 1000.

+

Also during this period, Carlson took up running and triathlon. On a + freelance assignment for the LA Times covering the 1993 Hawaiian Ironman, + Dan Empfield lent the newbie a bike to ride on the Queen K and thus he was + forever hooked on the magnificent sport.

+

From 1994 to 1999, Carlson wrote and took pictures for various multisport + magazines including Triathlete, Competitor, Multisport and Winning. From + 1999 to July 2008, he moved to Boulder and served as editor and then senior + correspondent for Inside Triathlon. Since then, he happily signed on with + the Slowtwitch crew.

+
+
+ +

Jonathan Toker - Slowtwitch Science Editor: Jonathan is an elite-level runner and triathlete + who hails from Canada and lives in Southern California. He received a Ph.D. + in organic chemistry from The Scripps Research Institute in 2001, and raced + in the professional ranks as a triathlete and runner for 5 years. A 9-time + Ironman finisher, with a personal best of 9:13, Jonathan recently completed + the arduous 6-day TransRockies running stage race and continues to seek new + athletic challenges. Dr. Toker worked as a scientist in the biotech + industry for 5 years prior to launching his unique SaltStick Electrolyte Capsule and Dispenser lineup.

+
+
+ +

Tanya Williams - Slowtwitch Business Manager: Tanya comes from a background as a technical writer but her chief tasks, + beyond managing the office, are Social Secretary and Whip Cracker.

+

Also among Tanya's duties are those that attend being Mrs. Slowman, so, the + two horses, seven dogs, and one husband are fed, housed, made presentable to + guests and visitors, and kept from barking and braying and otherwise + annoying the neighbors.

+
+
+ +

Mark Montgomery Xantusia Camp Host and Chief Storytelling Officer: Montomery's five-acre gentleman's ranch, adjacent to Empfield's ranchette + (the collection of properties is called "Xantusia"), is the place where + visiting pro triathletes, workshop attendees, manufacturers and others + headquarter themselves while on the property. "Monty's" job is to house, + feed, and ride and run with with guests of Xantusia, and to continue his + quest for the perfect margarita.

+

Monty began his multisport career in the early 70's as a lifeguard working + the beaches of Los Angeles County. His forte was the "Lifeguard Ironman" + which included swimming, running, paddling, rowing, and kayaking. + In 1978 he began training for his first triathlon and won his first 3 races. + Since then Mark has over 60 multisport wins in over 400 races during a 15 + year pro career as a triathlete and bike racer.

+

Montgomvery has also worked "behind the camera" in triathlon, as promoter + and race director of over 50 races, including the Los Angeles Triathlon + Series (ongoing since 1982). He founded the Triaction Sports triathlon shop + in the late 80's, which at the time was the shop to the stars and + headquarters for the latest in new technology. He worked closely with Dan + Empfield in designing the tri-specific bike and wetsuit.

+

Montgomery went back to work for the L.A. County Fire Department in 1994 as + a full time lifeguard retiring in 2003. He had a pacemaker installed in + 2001, and after 18 months of recovery got himself back in shape and + qualified for the U.S. Worlds team in the 45+ age group. He competed on the + U.S. team in both New Zealand and Portugal (World Championship sites for + 2003 and 2004).

+
+
+
+
+
<%-- end content --%> +
+ +
+
+ +
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/common/templates/agreement.html b/site/common/templates/agreement.html new file mode 100644 index 0000000..bbb994a --- /dev/null +++ b/site/common/templates/agreement.html @@ -0,0 +1,60 @@ + + + + <%site_title%>: User Agreement +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> +
+ <%include include_header.html%> + +
+ <%~set title_loop = Links::Build::build('title', 'User Agreement')%> + <%include include_breadcrumb.html%> +
+ + +
<%-- start content--%> +
+

User Agreement

+ +
+
+

THIS AGREEMENT BINDS YOU

+

Slowtwitch, Slowtwitch.com, and to its reader FORUM will cumulatively be called "Service". By using this Service, you agree to be bound by all of the terms of this Agreement. We reserve the right to change the terms of this Agreement or to modify features of this Service at any time. By registering for this Service and/or continuing to use this Service after the posting of Notices regarding such changes, you agree to be bound by such changes.

+

WHAT YOU SEE AND READ ON THESE PAGES IS COPYRIGHT PROTECTED

+

This Service (including, but not limited to, text, photographs, graphics and video content), except as noted in our Privacy Policy statement on FORUM authorship, is protected by copyright as a collective work or compilation under the copyright laws of the United States and other countries. All individual articles, content and other elements comprising this Service are also copyrighted works. You must abide by all additional copyright notices or restrictions contained in this Service. Reproduction of material from any Slowtwitch.com page without written permission by the Slowtwitch publisher (Dan Empfield) is strictly prohibited.

+

>IF YOU FEEL YOUR COPYRIGHT IS BEING ABUSED, PLEASE CONTACT US

+

Just as Slowtwitch requires users to respect our copyrights, and those of our affiliates and partners, we respect the copyrights of others. If you believe in good faith that your copyrighted work has been reproduced on our site without authorization in a way that constitutes copyright infringement, please notify the Slowtwitch publisher via the contact information provided at the bottom of most Slowtwitch pages.

+ +

YOU ARE RESOPONSIBLE FOR YOUR STATEMENTS

+

You are responsible for all statements made or materials posted under your account, including liability for harm caused by such statements or materials. You may not transfer, sell, or otherwise assign your rights or obligations under this Agreement. By posting content on the Service, a user is giving Slowtwitch the right to display such content on the Service

+

YOU HOLD US HARMLESS FOR YOUR BAD ACTS

+

You agree to indemnify and hold harmless Slowtwitch, Slowtwitch.com, and its owners, directors, officers, managers, authors, contractors, employees, agents, and licensors, from and against all losses, expenses, damages and costs, including reasonable attorneys' fees, resulting from any violation of this Agreement, or the failure to fulfill any obligations relating to your account incurred by you or any other person using your account. We reserve the right to take over the exclusive defense of any claim for which we are entitled to indemnification under this Section. In such event, you shall provide us with such cooperation as is reasonably requested by us.

+

THIS SERVICE IS AVAILABLE "AS IS"

+

We do not warrant that this Service will be uninterrupted or error-free. There may be delays, omissions, interruptions and inaccuracies in the news, information or other materials available through this Service. We are not responsible for the availability or content of other services that may be linked to this Service. Although we intend to take reasonable steps to prevent the introduction of viruses, worms, "Trojan horses" or other destructive materials to this Service, we do not guarantee or warrant that this Service or materials that may be downloaded from this Service do not contain such destructive features. We are not liable for any damages or harm attributable to such features. If you rely on this Service and any materials available through this Service, you do so solely at your own risk.

+

This Agreement shall be governed by the laws of the United States and the State of California. BY CONTINUING TO READ AND USE SLOWTWITCH.COM, YOU AGREE TO ABIDE BY THE TERMS OF THIS AGREEMENT.

+ +
+
+
+
<%-- end content --%> +
+ +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/common/templates/chris_ad_zone.html b/site/common/templates/chris_ad_zone.html new file mode 100644 index 0000000..b320354 --- /dev/null +++ b/site/common/templates/chris_ad_zone.html @@ -0,0 +1,52 @@ + + + + <%site_title || 'slowtwitch.com'%>: Responsive Video Ad Test +<%include include_common_head.html%> + + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> + +
+ <%include include_header.html%> + +
+
+ <%~set title_loop = Links::Build::build('title', 'About Us')%> + <%include include_breadcrumb.html%> +
+ + +
<%-- start content--%> +
+

Responsive Ad Test

+
+
+ + + + + +
+
+
+
<%-- end content --%> +
+ +
+
+ +
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/common/templates/comingsoon.html b/site/common/templates/comingsoon.html new file mode 100644 index 0000000..ed390c2 --- /dev/null +++ b/site/common/templates/comingsoon.html @@ -0,0 +1,36 @@ + +<%set static_url = 'http://forum.slowtwitch.com/static'%> + + + <%site_title || 'slowtwitch.com'%>: Comming soon +<%include include_common_head.html%> + + +
+ <%include include_header.html%> + +
+
+ + + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = 'Photos & Videos'%> + <%include include_breadcrumb.html%> + +
<%-- start content--%> +

Photos & Videos

+
+
+
+

Stay tuned updated photo & video galleries coming to Slowtwitch soon!

+ +
<%-- end content --%> +
+ +
+ <%include include_footer.html%> +
+ + diff --git a/site/common/templates/fb_sdk.html b/site/common/templates/fb_sdk.html new file mode 100644 index 0000000..061428d --- /dev/null +++ b/site/common/templates/fb_sdk.html @@ -0,0 +1,8 @@ +
+ \ No newline at end of file diff --git a/site/common/templates/google_analytics.html b/site/common/templates/google_analytics.html new file mode 100644 index 0000000..0b9d6a5 --- /dev/null +++ b/site/common/templates/google_analytics.html @@ -0,0 +1,30 @@ + + + + + + + + + \ No newline at end of file diff --git a/site/common/templates/google_map_individual.html b/site/common/templates/google_map_individual.html new file mode 100644 index 0000000..c7c73d2 --- /dev/null +++ b/site/common/templates/google_map_individual.html @@ -0,0 +1,21 @@ + + + + + diff --git a/site/common/templates/include_breadcrumb.html b/site/common/templates/include_breadcrumb.html new file mode 100644 index 0000000..8a1b007 --- /dev/null +++ b/site/common/templates/include_breadcrumb.html @@ -0,0 +1,6 @@ + diff --git a/site/common/templates/include_footer.html b/site/common/templates/include_footer.html new file mode 100644 index 0000000..36306b7 --- /dev/null +++ b/site/common/templates/include_footer.html @@ -0,0 +1,51 @@ + diff --git a/site/common/templates/include_global_head.html b/site/common/templates/include_global_head.html new file mode 100644 index 0000000..3b43410 --- /dev/null +++ b/site/common/templates/include_global_head.html @@ -0,0 +1,64 @@ + + + + + + + + + +<%~-- FIXME download google fonts --%> + +<%~set mini_version = '41'%> +<%~if not minify%><%set mini_version = 0%><%endif%> + +<%~if not mini_version%> + + + +<%~endif%> + +<%~if slowtwitch_forum%> + +<%~if mini_version%> + + +<%~if page_id ne 'category_list'%> + +<%~endif%> +<%~else%> + + + + +<%~endif%> + +<%~else%> +<%~if mini_version%> + +<%~else%> + + +<%~endif%> +<%~endif%> + + diff --git a/site/common/templates/include_global_js.html b/site/common/templates/include_global_js.html new file mode 100644 index 0000000..3ad234e --- /dev/null +++ b/site/common/templates/include_global_js.html @@ -0,0 +1,143 @@ +<%~if mini_version%> + +<%~if not slowtwitch_forum%> + +<%~endif%> +<%~else%> + + + + + + +<%~if not slowtwitch_forum%> + + +<%~endif%> +<%~endif%> + + \ No newline at end of file diff --git a/site/common/templates/include_header.html b/site/common/templates/include_header.html new file mode 100644 index 0000000..22802cb --- /dev/null +++ b/site/common/templates/include_header.html @@ -0,0 +1,310 @@ +
+
+
+ Swim + Bike + Run +
+
+ + +
+ + + +
+ +
+ + <%~if slowtwitch_forum%> +
+
+ <%~set adsize = '728x90'%> + <%include include_ad.html%> +
+
+ <%~else%> +
+
+ <%~set adsize = '728x90'%> + <%include include_ad.html%> +
+
+ <%~endif%> + <%~set search_url = "#"%> + <%~if slowtwitch_forum%> + <%~set search_url = GForum::SEO::url()%> + <%~else%> + <%~set search_url = $config.db_cgi_url%><%set search_url .= '/search.cgi'%> + <%~endif%> + + +
+ + +
diff --git a/site/common/templates/include_social.html b/site/common/templates/include_social.html new file mode 100644 index 0000000..2fea91e --- /dev/null +++ b/site/common/templates/include_social.html @@ -0,0 +1,5 @@ +Twitter +Vimeo +Facebook + +Youtube diff --git a/site/common/templates/include_subscribe.html b/site/common/templates/include_subscribe.html new file mode 100644 index 0000000..ce755f8 --- /dev/null +++ b/site/common/templates/include_subscribe.html @@ -0,0 +1,28 @@ + + + +
+
+
+
+ + + +
+ +
+ + +
+ +
+
+
+
+ + diff --git a/site/common/templates/local/404.html b/site/common/templates/local/404.html new file mode 100644 index 0000000..f50062a --- /dev/null +++ b/site/common/templates/local/404.html @@ -0,0 +1,41 @@ + + + + <%site_title || 'slowtwitch.com'%>: Page Not Found +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> + +
+ <%include include_header.html%> + +
+
+ <%~set title_loop = Links::Build::build('title', 'Page Not Found')%> + <%include include_breadcrumb.html%> +
+ +
<%-- start content--%> +
+

Page Not Found

+
+
+ It seems you've ventured off course. Why don't you head back to the transition area? +
+
+
+
<%-- end content --%> +
+ +
+
+ +
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/common/templates/local/aboutus.html b/site/common/templates/local/aboutus.html new file mode 100644 index 0000000..13e67be --- /dev/null +++ b/site/common/templates/local/aboutus.html @@ -0,0 +1,152 @@ + + + + <%site_title || 'slowtwitch.com'%>: About Us +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> + +
+ <%include include_header.html%> + +
+
+ <%~set title_loop = Links::Build::build('title', 'About Us')%> + <%include include_breadcrumb.html%> +
+ +
<%-- start content--%> +
+

About Us

+
+
+
+ +

Dan Empfield, Publisher: Born in California in 1957, Empfield is a graduate of South Tahoe High School and the University of Nevada (Reno), earning a Bachelor of Science in biology.

+

Empfield competed in his first triathlon in 1978, and in the first Ironman held on Hawaii's Big Island, in 1981.

+

He designed the original triathlon wetsuit, and founded wetsuit manufacturer Quintana Roo in 1987.

+

In 1989 Empfield debuted another design, the first bike "built from the aerobars back." The first Quintana Roo Superform had 650c wheels front and back, and an 80-degree seat angle. Today's modern triathlon and time trial bikes are progeny of the Superform.

+

He sold Quintana Roo to Saucony, Inc., in 1995, and stayed on to run Saucony's bike division - which included Quintana Roo and Merlin - for four years. He left in 1999 and founded Slowtwitch.com.

+

In 2003 Empfield developed the F.I.S.T. Tri Bike Fit System for triathletes, featuring "stack" and "reach," a set of bike fit metrics now popular among tri and road bike manufacturers and fitters. He's given fit clinics and workshops all over the United States and Europe. Today many modern fit systems employ the protocol, mechanics and metrics that are the backbone of the F.I.S.T. fit system.

+

Empfield was inducted into the Triathlete Magazine Hall of Fame in 2004, and received the World Open Water Swimming Association's (WOWSA) lifetime achievement award in 2010.

+

Empfield is a former USA Triathlon board member and, along with Lew Kidder, helped craft a set of USAT bylaws that reformed voting procedures, and redrew USAT voting regions. Most of those bylaws and districts remain in force today.

+

Empfield hosted the U.S. Professional National Triathlon Championship in 1998, held in accordance with Olympic draft-legal rules. This race was part of the first national series devoted to Olympic-rules triathlon - a series Empfield produced and funded.

+

Empfield continues to write for, and publish, Slowtwitch.com. He still swims, bikes, runs and races several times a year. He lives with wife Tanya, dogs, and horses on 8 acres in the San Gabriel Mountains of Southern California.

+
+
+ +

Ryan Heisler, Editor-in-Chief: Originally from Massachusetts, Heisler earned his undergraduate in broadcast journalism at Emerson College and his law degree from the University of Maine School of Law. During his time in school, he began his career in the specialty run industry at City Sports, a few hundred meters past the finish line of the Boston Marathon.          

+

While in law school, Heisler discovered his passion laid in working in the endurance sports space, and eventually grew into management positions with Maine Running Company (now Fleet Feet Maine Running). During this time, healso began dabbling in triathlon, eventually joining the age-group team of Revolution3 Triathlon. As the series grew, he came on board to help develop a retail concept store out of headquarters in Manassas, VA.        

+

In 2014, Heisler fractured his spine and suffered a traumatic brain injury in a cycling crash. Shortly thereafter, he left the running industry and began his writing career in the endurance sports space. In 2016, he joined Slowtwitch.com as Social Media Manager, and has steadily grown in responsibilities since. 

+

In addition to his work for Slowtwitch.com, Heisler is also one of the partners in Slowtwitch Media House. He lives in southwestern New Hampshire with his wife, Kelly, his daughter, Ivy, Owen Otter, and various farm animals that Kelly and Ivy keep attempting to acquire.

+
+ +
+ +

Jordan Rapp, Chief Technology Officer : After spending four years studying mechanical and aerospace engineering at Princeton University, Jordan Rapp only really came to enjoy it as a result of triathlon. A novice in all three sports, the idea of using equipment to go faster seemed like a great idea to a college rower used to racing for six minutes. 2012 was his 10th season of racing, and 7th as a pro, though he's enjoying a second crack at this whole triathlon thing and at life after being nearly killed in a hit-and-run in March of 2010 while riding his bicycle.

+

He earned his rainbow stripes by winning the ITU Long Distance World Championships in 2011. He's a five-time Ironman winner (US Championships in New York '12, Texas '12, Canada '09 & '11, and Arizona '09). And his first trip to the big dance in Kona in 2012 saw him finishing lucky number 13.

+

As CTO, Jordan is in charge of wrangling the multi-headed hydra of a website that is Slowtwitch.com. Despite being a somewhat Sisyphean ordeal, he nevertheless does his best to make sure Slowtwitch walks and talks the way it is supposed to. If you like Slowtwitch, feel free to let him know. If you don't, please tell Dan instead. Armed with his trusty Macbook Air, he tries to stay one step ahead of his ever growing “To Do” list.

+

More than a bit of a technophile, Jordan does the occasional product review for the site. Nothing makes him happier than cardboard boxes, with the possible exception of swim goggles, so this is the opiate that offsets the occasionally overwhelming job of coding the site.

+

Jordan also co-instructed the F.I.S.T. bike fit workshops with Dan for several years, where they helped people find enlightenment aboard a bicycle, before taking a leave of absence after the birth of his son. In addition to F.I.S.T.ing people, you can regularly find him around Xantusia where his favorite activity is climbing the mountain to Wrightwood, which is just about the only time Dan lets him away from his laptop.

+

Jordan is married to Canadian Olympian Jill Savege (Athens '04), winner of three ITU World Cups, and the holder of the best Kona debut in the house with a 10th place in 2001. They have one child - Quentin Thomas - born in 2011.

+
+
+ +

Timothy Carlson, Senior Correspondent: Born in Daytona Beach, Florida, Carlson is a graduate of Seabreeze Senior High where he set a school record for most touchdown passes caught (and dropped) and ran a leg on the state champion 880 relay in which he did not drop the baton. He also cleared 8 feet 6 inches in the pole vault, landing six feet past the pit, tumbled into a beautiful girl and subsequently asked her out. At Harvard College, he punted for the freshman football squad until he sailed a kick + into a stuff wind which came backward, forcing him to down the ball before + it settled in the Crimson's end zone. In his sophomore year, he took + pictures for the Harvard Crimson daily newspaper and was on assignment + inside University Hall during a student occupation when he was arrested with + the demonstrators by Cambridge police. After successfully smuggling his + pictures out of jail, several of his photos appeared in Life Magazine + coverage of the event.

+

In an eclectic post graduate period, he taught expository writing and worked + as a freelance writer and photographer during which he covered a wide range + of events including the Super Bowl, the Democratic and Republican + conventions, a coal miner's strike in West Virginia, the Daytona 500, NBA + finals and Palestinian refugees in Lebanon. In his most exciting assignment, + he served as a co-driver for the Polish Racing Drivers of America Ford + Econoline van in the original Cannonball Sea-to-Shining-Sea Memorial Trophy + Dash race from New York to Redondo Beach, California. With a makeshift + 120-gallon fuel tank, no one smoked and the first gas stop was in Missouri.

+

Finally gaining regular employment, he worked as a staff writer at the Los + Angeles Herald Examiner for 10 years, during which he wrote for the Style, + Sports, News, Magazine and editorial sections. During one feature + assignment, he rode as passenger at Riverside raceway in an off road truck + which flipped end over end. In another assignment to cover a murder in south + central LA, he was abducted and held overnight by a crack addict. At the + Herald's demise, he went to work for the LA Bureau of TV Guide magazine. + During that four year stint, he wrapped up a 10-year career racing off road + motorcycles (which included one spectacular crash which required he be + airlifted from Erendira to Scripps Hospital in San Diego)co-riding to a 5th + place finish in the Open Motorcycle division in the 1992 Baja 1000.

+

Also during this period, Carlson took up running and triathlon. On a + freelance assignment for the LA Times covering the 1993 Hawaiian Ironman, + Dan Empfield lent the newbie a bike to ride on the Queen K and thus he was + forever hooked on the magnificent sport.

+

From 1994 to 1999, Carlson wrote and took pictures for various multisport + magazines including Triathlete, Competitor, Multisport and Winning. From + 1999 to July 2008, he moved to Boulder and served as editor and then senior + correspondent for Inside Triathlon. Since then, he happily signed on with + the Slowtwitch crew.

+
+
+ +

Tanya Williams, Business Manager: Tanya comes from a background as a technical writer but her chief tasks, + beyond managing the office, are Social Secretary and Whip Cracker.

+

Also among Tanya's duties are those that attend being Mrs. Slowman, so, the + two horses, seven dogs, and one husband are fed, housed, made presentable to + guests and visitors, and kept from barking and braying and otherwise + annoying the neighbors.

+
+
+ +

Mark Montgomery, Xantusia Host and Storyteller: Montgomery's five-acre gentleman's ranch, adjacent to Empfield's ranchette + (the collection of properties is called "Xantusia"), is the place where + visiting pro triathletes, workshop attendees, manufacturers and others + headquarter themselves while on the property. "Monty's" job is to house, + feed, and ride and run with with guests of Xantusia, and to continue his + quest for the perfect margarita.

+

Monty began his multisport career in the early 70's as a lifeguard working + the beaches of Los Angeles County. His forte was the "Lifeguard Ironman" + which included swimming, running, paddling, rowing, and kayaking. + In 1978 he began training for his first triathlon and won his first 3 races. + Since then Mark has over 60 multisport wins in over 400 races during a 15 + year pro career as a triathlete and bike racer.

+

Montgomery has also worked "behind the camera" in triathlon, as promoter + and race director of over 50 races, including the Los Angeles Triathlon + Series (ongoing since 1982). He founded the Triaction Sports triathlon shop + in the late 80's, which at the time was the shop to the stars and + headquarters for the latest in new technology. He worked closely with Dan + Empfield in designing the tri-specific bike and wetsuit.

+

Montgomery went back to work for the L.A. County Fire Department in 1994 as + a full time lifeguard retiring in 2003. He had a pacemaker installed in + 2001, and after 18 months of recovery got himself back in shape and + qualified for the U.S. Worlds team in the 45+ age group. He competed on the + U.S. team in both New Zealand and Portugal (World Championship sites for + 2003 and 2004).

+
+
+

Eric Wynn, President: Wynn is returning from a brief hiatus from full-time photography to assist his family. He joins Slowtwitch to spearhead the new Slowtwitch Media House and comes to us with over a decade of digital content creation experience. He has thousands of digital and print credits to his name, and has helped local, regional and global companies with their brand awareness through digital visuals and marketing channels. His unique ability to refresh and innovate brings a new spark of visual life to Slowtwitch.com and the endurance space. Wynn's background with professional athletes, events and business surroundings allows him to be comfortable in any place or setting. Some of his more noteworthy past or present clients include: Powerbar, Nestle, Shimano, Zipp/Sram, Lifetime Fitness, Toyota, Re 3, Specialized, TYR, Trek, Triathlete Magazine, Advanced Sports, Inc., Hachette Book Group, CEP, Compex, CytoSport, and Wintech Racing.

+
+
+

 

+
+
+
+
+
<%-- end content --%> +
+ +
+
+ +
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/common/templates/local/advertise.html b/site/common/templates/local/advertise.html new file mode 100644 index 0000000..be50b56 --- /dev/null +++ b/site/common/templates/local/advertise.html @@ -0,0 +1,44 @@ + + + + <%site_title || 'slowtwitch.com'%>: About Us +<%include include_common_head.html%> + + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> + +
+ <%include include_header.html%> + +
+
+ <%~set title_loop = Links::Build::build('title', 'Advertise')%> + <%include include_breadcrumb.html%> +
+ +
<%-- start content--%> +
+

Advertise

+
+
+
+
+
+
+
<%-- end content --%> +
+ +
+
+ +
+ + <%include include_footer.html%> +
+ + +<%~include include_global_js.html%> + diff --git a/site/common/templates/local/agreement.html b/site/common/templates/local/agreement.html new file mode 100644 index 0000000..d2b38c2 --- /dev/null +++ b/site/common/templates/local/agreement.html @@ -0,0 +1,60 @@ + + + + <%site_title%>: User Agreement +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> +
+ <%include include_header.html%> + +
+ <%~set title_loop = Links::Build::build('title', 'User Agreement')%> + <%include include_breadcrumb.html%> +
+ + +
<%-- start content--%> +
+

User Agreement

+ +
+
+

THIS AGREEMENT BINDS YOU

+

Slowtwitch, Slowtwitch.com, and to its reader FORUM will cumulatively be called "Service". By using this Service, you agree to be bound by all of the terms of this Agreement. We reserve the right to change the terms of this Agreement or to modify features of this Service at any time. By registering for this Service and/or continuing to use this Service after the posting of Notices regarding such changes, you agree to be bound by such changes.

+

WHAT YOU SEE AND READ ON THESE PAGES IS COPYRIGHT PROTECTED

+

This Service (including, but not limited to, text, photographs, graphics and video content), except as noted in our Privacy Policy statement on FORUM authorship, is protected by copyright as a collective work or compilation under the copyright laws of the United States and other countries. All individual articles, content and other elements comprising this Service are also copyrighted works. You must abide by all additional copyright notices or restrictions contained in this Service. Reproduction of material from any Slowtwitch.com page without written permission by the Slowtwitch publisher (Dan Empfield) is strictly prohibited.

+

IF YOU FEEL YOUR COPYRIGHT IS BEING ABUSED, PLEASE CONTACT US

+

Just as Slowtwitch requires users to respect our copyrights, and those of our affiliates and partners, we respect the copyrights of others. If you believe in good faith that your copyrighted work has been reproduced on our site without authorization in a way that constitutes copyright infringement, please notify the Slowtwitch publisher via the contact information provided at the bottom of most Slowtwitch pages.

+ +

YOU ARE RESPONSIBLE FOR YOUR STATEMENTS

+

You are responsible for all statements made or materials posted under your account, including liability for harm caused by such statements or materials. You may not transfer, sell, or otherwise assign your rights or obligations under this Agreement. By posting content on the Service, a user is giving Slowtwitch the right to display such content on the Service

+

YOU HOLD US HARMLESS FOR YOUR BAD ACTS

+

You agree to indemnify and hold harmless Slowtwitch, Slowtwitch.com, and its owners, directors, officers, managers, authors, contractors, employees, agents, and licensors, from and against all losses, expenses, damages and costs, including reasonable attorneys' fees, resulting from any violation of this Agreement, or the failure to fulfill any obligations relating to your account incurred by you or any other person using your account. We reserve the right to take over the exclusive defense of any claim for which we are entitled to indemnification under this Section. In such event, you shall provide us with such cooperation as is reasonably requested by us.

+

THIS SERVICE IS AVAILABLE "AS IS"

+

We do not warrant that this Service will be uninterrupted or error-free. There may be delays, omissions, interruptions and inaccuracies in the news, information or other materials available through this Service. We are not responsible for the availability or content of other services that may be linked to this Service. Although we intend to take reasonable steps to prevent the introduction of viruses, worms, "Trojan horses" or other destructive materials to this Service, we do not guarantee or warrant that this Service or materials that may be downloaded from this Service do not contain such destructive features. We are not liable for any damages or harm attributable to such features. If you rely on this Service and any materials available through this Service, you do so solely at your own risk.

+

This Agreement shall be governed by the laws of the United States and the State of California. BY CONTINUING TO READ AND USE SLOWTWITCH.COM, YOU AGREE TO ABIDE BY THE TERMS OF THIS AGREEMENT.

+ +
+
+
+
<%-- end content --%> +
+ +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/common/templates/local/cookies.html b/site/common/templates/local/cookies.html new file mode 100644 index 0000000..2d47d06 --- /dev/null +++ b/site/common/templates/local/cookies.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/site/common/templates/local/include_footer.html b/site/common/templates/local/include_footer.html new file mode 100644 index 0000000..5d95901 --- /dev/null +++ b/site/common/templates/local/include_footer.html @@ -0,0 +1,55 @@ + + + diff --git a/site/common/templates/local/include_global_head.html b/site/common/templates/local/include_global_head.html new file mode 100644 index 0000000..8872f97 --- /dev/null +++ b/site/common/templates/local/include_global_head.html @@ -0,0 +1,77 @@ + + + + + + + + + + + +<%~-- Google fonts --%> + + +<%~set mini_version = '41'%> +<%~if not minify%><%set mini_version = 0%><%endif%> + +<%~if not mini_version%> + + + +<%~endif%> + +<%~if slowtwitch_forum%> + +<%~if mini_version%> + + +<%~if page_id ne 'category_list'%> + +<%~endif%> +<%~else%> + + + + +<%~endif%> + +<%~else%> +<%~if mini_version%> + +<%~else%> + + +<%~endif%> +<%~endif%> + + + +<%google_tag_manager%> +<%google_analytics%> +<%advertserve_ajax%> + +<%~if slowtwitch_forum%> +<%~if $forum_id == 11 %> +<%ezoic%> +<%~endif%> +<%~endif%> diff --git a/site/common/templates/local/include_header.html b/site/common/templates/local/include_header.html new file mode 100644 index 0000000..4be123b --- /dev/null +++ b/site/common/templates/local/include_header.html @@ -0,0 +1,311 @@ + + +
+
+
+ Swim + Bike + Run +
+
+ + +
+ + + +
+ +
+ + <%~if slowtwitch_forum%> +
+
+ <%~set adsize = '728x90'%> + <%include include_ad.html%> +
+
+ <%~else%> +
+
+ <%~set adsize = '728x90'%> + <%include include_ad.html%> +
+
+ <%~endif%> + <%~set search_url = "#"%> + <%~if slowtwitch_forum%> + <%~set search_url = GForum::SEO::url()%> + <%~else%> + <%~set search_url = $config.db_cgi_url%><%set search_url .= '/search.cgi'%> + <%~endif%> + + +
+ + +
diff --git a/site/common/templates/local/include_subscribe.html b/site/common/templates/local/include_subscribe.html new file mode 100644 index 0000000..ce755f8 --- /dev/null +++ b/site/common/templates/local/include_subscribe.html @@ -0,0 +1,28 @@ + + + +
+
+
+
+ + + +
+ +
+ + +
+ +
+
+
+
+ + diff --git a/site/common/templates/local/inventory.html b/site/common/templates/local/inventory.html new file mode 100644 index 0000000..d2b38c2 --- /dev/null +++ b/site/common/templates/local/inventory.html @@ -0,0 +1,60 @@ + + + + <%site_title%>: User Agreement +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> +
+ <%include include_header.html%> + +
+ <%~set title_loop = Links::Build::build('title', 'User Agreement')%> + <%include include_breadcrumb.html%> +
+ + +
<%-- start content--%> +
+

User Agreement

+ +
+
+

THIS AGREEMENT BINDS YOU

+

Slowtwitch, Slowtwitch.com, and to its reader FORUM will cumulatively be called "Service". By using this Service, you agree to be bound by all of the terms of this Agreement. We reserve the right to change the terms of this Agreement or to modify features of this Service at any time. By registering for this Service and/or continuing to use this Service after the posting of Notices regarding such changes, you agree to be bound by such changes.

+

WHAT YOU SEE AND READ ON THESE PAGES IS COPYRIGHT PROTECTED

+

This Service (including, but not limited to, text, photographs, graphics and video content), except as noted in our Privacy Policy statement on FORUM authorship, is protected by copyright as a collective work or compilation under the copyright laws of the United States and other countries. All individual articles, content and other elements comprising this Service are also copyrighted works. You must abide by all additional copyright notices or restrictions contained in this Service. Reproduction of material from any Slowtwitch.com page without written permission by the Slowtwitch publisher (Dan Empfield) is strictly prohibited.

+

IF YOU FEEL YOUR COPYRIGHT IS BEING ABUSED, PLEASE CONTACT US

+

Just as Slowtwitch requires users to respect our copyrights, and those of our affiliates and partners, we respect the copyrights of others. If you believe in good faith that your copyrighted work has been reproduced on our site without authorization in a way that constitutes copyright infringement, please notify the Slowtwitch publisher via the contact information provided at the bottom of most Slowtwitch pages.

+ +

YOU ARE RESPONSIBLE FOR YOUR STATEMENTS

+

You are responsible for all statements made or materials posted under your account, including liability for harm caused by such statements or materials. You may not transfer, sell, or otherwise assign your rights or obligations under this Agreement. By posting content on the Service, a user is giving Slowtwitch the right to display such content on the Service

+

YOU HOLD US HARMLESS FOR YOUR BAD ACTS

+

You agree to indemnify and hold harmless Slowtwitch, Slowtwitch.com, and its owners, directors, officers, managers, authors, contractors, employees, agents, and licensors, from and against all losses, expenses, damages and costs, including reasonable attorneys' fees, resulting from any violation of this Agreement, or the failure to fulfill any obligations relating to your account incurred by you or any other person using your account. We reserve the right to take over the exclusive defense of any claim for which we are entitled to indemnification under this Section. In such event, you shall provide us with such cooperation as is reasonably requested by us.

+

THIS SERVICE IS AVAILABLE "AS IS"

+

We do not warrant that this Service will be uninterrupted or error-free. There may be delays, omissions, interruptions and inaccuracies in the news, information or other materials available through this Service. We are not responsible for the availability or content of other services that may be linked to this Service. Although we intend to take reasonable steps to prevent the introduction of viruses, worms, "Trojan horses" or other destructive materials to this Service, we do not guarantee or warrant that this Service or materials that may be downloaded from this Service do not contain such destructive features. We are not liable for any damages or harm attributable to such features. If you rely on this Service and any materials available through this Service, you do so solely at your own risk.

+

This Agreement shall be governed by the laws of the United States and the State of California. BY CONTINUING TO READ AND USE SLOWTWITCH.COM, YOU AGREE TO ABIDE BY THE TERMS OF THIS AGREEMENT.

+ +
+
+
+
<%-- end content --%> +
+ +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/common/templates/local/privacy.html b/site/common/templates/local/privacy.html new file mode 100644 index 0000000..094322a --- /dev/null +++ b/site/common/templates/local/privacy.html @@ -0,0 +1,69 @@ + + + + <%site_title%>: Privacy Policy +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> + +
+ <%include include_header.html%> + +
+
+ <%~set title_loop = Links::Build::build('title', 'Privacy Policy')%> + <%include include_breadcrumb.html%> +
+ + +
<%-- start content--%> +
+

Privacy Policy

+ +
+
+

Information we gather

+

The information we gather falls into two categories: 1) personal information you voluntarily supply when you complete a survey, register as a user on our FORUM, enter a contest or provide your e-mail address, and 2) tracking information collected as you navigate through our sites (cookies).

+

What Are Cookies?

+

Cookies are small text files that are stored in your device’s browser when you visit a website. Cookies make it possible to recognize you as the same user across one or more browsing sessions, and across one or more websites. We and third parties use cookies for a variety of purposes, including remembering your sign-in credentials and preferences so that you do not have to input them each time you log in; allowing us and third parties to customize your experience, including by determining the most relevant content and advertisements to show you on our websites and elsewhere online; and allowing us and third parties to monitor site traffic and performance.

+

Use of Information

+

If you have registered or signed up on Slowtwitch, we will not sell, rent, swap or authorize any third party to use your email address without your permission. This also applies to any information that personally identifies you, except as noted immediately below.

+

We may disclose personal information if we are required to do so by law or we in good faith believe that such action is necessary to (1) comply with the law or with legal process; (2) protect and defend our rights and property; (3) protect against misuse or unauthorized use of our website(s); or (4) protect the personal safety or property of our users or the public (among other things, this means that if you provide false information or attempt to pose as someone else, information about you may be disclosed as part of any investigation into your actions).

+

We do, however, share information about our audience in aggregate form. For example, we may want to know how long the average reader spends on our site, or which pages or features get the most attention. Slowtwitch may perform statistical analyses of readers and their reading patterns for product development purposes and to generally inform advertisers about the nature of our subscriber base. We may use demographic and preference information to allow advertising on our Web sites to be targeted, in aggregate, to the users for whom they are most pertinent.

+

Internet Based Advertising

+

Some of the ads you see on this web site are tailored to your interests and based on your activity online or in the applications on your mobile device. This type of ad tailoring — sometimes called "interest-based" or "online behavioral" advertising — is enabled through various technologies, including browser cookies, mobile advertising identifiers as well as other non-cookie technologies. To help protect your privacy, we are committed to providing you transparency and choice for these activities. For more information and to opt-out of interest-based advertising, please visit: http://optout.networkadvertising.org/?c=1#!/ +

Forum Use

+

Any information you disclose when posting to our FORUM, along with your message board screen name or ID, becomes public, and Slowtwitch has no control over how readers may use or treat what you post or divulge.

+

Slowtwitch Presence Wipeout

+

A user constructs his or her presence here, whether as a Forum personality, or with a business listing (Coach, Bike Fitter, Bike Shop, Run Store, Race). Should you want a persona you established here eliminated, email or private message us stating exactly what you built (business listing, forum personality) that you now want removed.

+

Email Correspondence

+

If, at any time, you prefer not to receive email newsletters or correspondence from us, simply follow the unsubscribe options at the bottom of each e-mail.

+

Amendments To This Policy

+

Our Privacy Policy may be amended from time to time. Any such changes will be posted on this Privacy Policy page.

+

Cookie Declaration

+ +
+
+
+
<%-- end content --%> +
+ +
+
+ +
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/common/templates/privacy.html b/site/common/templates/privacy.html new file mode 100644 index 0000000..9f245c5 --- /dev/null +++ b/site/common/templates/privacy.html @@ -0,0 +1,61 @@ + + + + <%site_title%>: Privacy Policy +<%include include_common_head.html%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> + +
+ <%include include_header.html%> + +
+
+ <%~set title_loop = Links::Build::build('title', 'Privacy Policy')%> + <%include include_breadcrumb.html%> +
+ + +
<%-- start content--%> +
+

Privacy Policy

+ +
+
+

Information we gather

+

The information we gather falls into two categories: 1) personal information you voluntarily supply when you complete a survey, register as a user on our FORUM, enter a contest or provide your e-mail address, and 2) tracking information collected as you navigate through our sites.

+

Use of Information

+

If you have registered or signed up on Slowtwitch, we will not sell, rent, swap or authorize any third party to use your email address without your permission. This also applies to any information that personally identifies you, except as noted immediately below.

+

We may disclose personal information if we are required to do so by law or we in good faith believe that such action is necessary to (1) comply with the law or with legal process; (2) protect and defend our rights and property; (3) protect against misuse or unauthorized use of our website(s); or (4) protect the personal safety or property of our users or the public (among other things, this means that if you provide false information or attempt to pose as someone else, information about you may be disclosed as part of any investigation into your actions).

+

We do, however, share information about our audience in aggregate form. For example, we may want to know how long the average reader spends on our site, or which pages or features get the most attention. Slowtwitch may perform statistical analyses of readers and their reading patterns for product development purposes and to generally inform advertisers about the nature of our subscriber base. We may use demographic and preference information to allow advertising on our Web sites to be targeted, in aggregate, to the users for whom they are most pertinent.

+

Forum Use

+

Any information you disclose when posting to our FORUM, along with your message board screen name or ID, becomes public, and Slowtwitch has no control over how readers may use or treat what you post or divulge.

+

Email Correspondence

+

If, at any time, you prefer not to receive email newsletters or correspondence from us, simply follow the unsubscribe options at the bottom of each e-mail.

+

Amendments To This Policy

+

Our Privacy Policy may be amended from time to time. Any such changes will be posted on this Privacy Policy page.

+
+
+
+
<%-- end content --%> +
+ +
+
+ +
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/fitters/class/rating/styles/rating.css b/site/fitters/class/rating/styles/rating.css new file mode 100644 index 0000000..eec6d79 --- /dev/null +++ b/site/fitters/class/rating/styles/rating.css @@ -0,0 +1,102 @@ +/* star rating code - use lists because its more semantic */ +/* No javascript required */ +/* all the stars are contained in one matrix to solve rollover problems with delay */ +/* the background position is just shifted to reveal the correct image. */ +/* the images are 16px by 16px and the background position will be shifted in negative 16px increments */ +/* key: B=Blank : O=Orange : G = Green * / +/*..... The Matrix ....... */ +/* colours ....Background position */ +/* B B B B B - (0 0)*/ +/* G B B B B - (0 -16px)*/ +/* G G B B B - (0 -32px)*/ +/* G G G B B - (0 -48px)*/ +/* G G G G B - (0 -64px)*/ +/* G G G G G - (0 -80px)*/ +/* O B B B B - (0 -96px)*/ +/* O O B B B - (0 -112px)*/ +/* O O O B B - (0 -128px)*/ +/* O O O O B - (0 -144px)*/ +/* O O O O O - (0 -160px)*/ + + +/* the default rating is placed as a background image in the ul */ +/* use the background position according to the table above to display the required images*/ +.rating{ + width:80px; + height:16px; + margin:0px 0px 20px 0px; + padding:0; + list-style:none; + float: left; + position:relative; + background: url(../images/star-matrix.gif) no-repeat 0 0; +} +ul.rating li { + cursor: pointer; + /*ie5 mac doesn't like it if the list is floated\*/ + float:left; + /* end hide*/ + text-indent:-999em; +} +ul.rating li a { + position:absolute; + left:0; + top:0; + width:16px; + height:16px; + text-decoration:none; + z-index: 200; +} +ul.rating li.one a {left:0} +ul.rating li.two a {left:16px;} +ul.rating li.three a {left:32px;} +ul.rating li.four a {left:48px;} +ul.rating li.five a {left:64px;} +ul.rating li a:hover { + z-index:2; + width:80px; + height:16px; + overflow:hidden; + left:0; + background: url(../images/star-matrix.gif) no-repeat 0 0 +} +ul.rating li.one a:hover {background-position:0 -96px;} +ul.rating li.two a:hover {background-position:0 -112px;} +ul.rating li.three a:hover {background-position:0 -128px} +ul.rating li.four a:hover {background-position:0 -144px} +ul.rating li.five a:hover {background-position:0 -160px} + +/* this is used to remove the hover affect */ +/* use the background position according to the table above to display the required images*/ +.rated{ + width:80px; + height:16px; + margin:0px 0px 20px 0px; + padding:0; + list-style:none; + float: left; + position:relative; + background: url(../images/star-matrix.gif) no-repeat 0 0; +} +ul.rated li { + cursor: pointer; + /*ie5 mac doesn't like it if the list is floated\*/ + float:left; + /* end hide*/ + text-indent:-999em; +} +ul.rated li.one a {left:0} +ul.rated li.two a {left:16px;} +ul.rated li.three a {left:32px;} +ul.rated li.four a {left:48px;} +ul.rated li.five a {left:64px;} + +/* add these classes to the ul to effect the change to the correct number of stars */ +.nostar {background-position:0 0} +.onestar {background-position:0 -16px} +.twostar {background-position:0 -32px} +.threestar {background-position:0 -48px} +.fourstar {background-position:0 -64px} +.fivestar {background-position:0 -80px} +/* end rating code */ +h3{margin:0 0 2px 0;font-size:110%} \ No newline at end of file diff --git a/site/fitters/rating/styles/rating.css b/site/fitters/rating/styles/rating.css new file mode 100644 index 0000000..eec6d79 --- /dev/null +++ b/site/fitters/rating/styles/rating.css @@ -0,0 +1,102 @@ +/* star rating code - use lists because its more semantic */ +/* No javascript required */ +/* all the stars are contained in one matrix to solve rollover problems with delay */ +/* the background position is just shifted to reveal the correct image. */ +/* the images are 16px by 16px and the background position will be shifted in negative 16px increments */ +/* key: B=Blank : O=Orange : G = Green * / +/*..... The Matrix ....... */ +/* colours ....Background position */ +/* B B B B B - (0 0)*/ +/* G B B B B - (0 -16px)*/ +/* G G B B B - (0 -32px)*/ +/* G G G B B - (0 -48px)*/ +/* G G G G B - (0 -64px)*/ +/* G G G G G - (0 -80px)*/ +/* O B B B B - (0 -96px)*/ +/* O O B B B - (0 -112px)*/ +/* O O O B B - (0 -128px)*/ +/* O O O O B - (0 -144px)*/ +/* O O O O O - (0 -160px)*/ + + +/* the default rating is placed as a background image in the ul */ +/* use the background position according to the table above to display the required images*/ +.rating{ + width:80px; + height:16px; + margin:0px 0px 20px 0px; + padding:0; + list-style:none; + float: left; + position:relative; + background: url(../images/star-matrix.gif) no-repeat 0 0; +} +ul.rating li { + cursor: pointer; + /*ie5 mac doesn't like it if the list is floated\*/ + float:left; + /* end hide*/ + text-indent:-999em; +} +ul.rating li a { + position:absolute; + left:0; + top:0; + width:16px; + height:16px; + text-decoration:none; + z-index: 200; +} +ul.rating li.one a {left:0} +ul.rating li.two a {left:16px;} +ul.rating li.three a {left:32px;} +ul.rating li.four a {left:48px;} +ul.rating li.five a {left:64px;} +ul.rating li a:hover { + z-index:2; + width:80px; + height:16px; + overflow:hidden; + left:0; + background: url(../images/star-matrix.gif) no-repeat 0 0 +} +ul.rating li.one a:hover {background-position:0 -96px;} +ul.rating li.two a:hover {background-position:0 -112px;} +ul.rating li.three a:hover {background-position:0 -128px} +ul.rating li.four a:hover {background-position:0 -144px} +ul.rating li.five a:hover {background-position:0 -160px} + +/* this is used to remove the hover affect */ +/* use the background position according to the table above to display the required images*/ +.rated{ + width:80px; + height:16px; + margin:0px 0px 20px 0px; + padding:0; + list-style:none; + float: left; + position:relative; + background: url(../images/star-matrix.gif) no-repeat 0 0; +} +ul.rated li { + cursor: pointer; + /*ie5 mac doesn't like it if the list is floated\*/ + float:left; + /* end hide*/ + text-indent:-999em; +} +ul.rated li.one a {left:0} +ul.rated li.two a {left:16px;} +ul.rated li.three a {left:32px;} +ul.rated li.four a {left:48px;} +ul.rated li.five a {left:64px;} + +/* add these classes to the ul to effect the change to the correct number of stars */ +.nostar {background-position:0 0} +.onestar {background-position:0 -16px} +.twostar {background-position:0 -32px} +.threestar {background-position:0 -48px} +.fourstar {background-position:0 -64px} +.fivestar {background-position:0 -80px} +/* end rating code */ +h3{margin:0 0 2px 0;font-size:110%} \ No newline at end of file diff --git a/site/fitters/styles.css b/site/fitters/styles.css new file mode 100644 index 0000000..d2bd5d1 --- /dev/null +++ b/site/fitters/styles.css @@ -0,0 +1,545 @@ +/* Defaults */ +body { +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +color: #000; +padding: 0; +margin: 15px 0 5px; +} + +.indent { +margin-left: 10px; +} + +.indentlarge { +margin-left: 30px; +} + +.indenthuge { +margin-left: 50px; +} + +.rightside { +text-align: right; +} + +form { +margin: 0; +padding: 0; +} + +/* Links +a:link { +color: #000099; +text-decoration : none; +padding : 0; +margin : 0; +} +a:visited { +color: #000099; +text-decoration : none; +} +a:hover { +color: #000000; +text-decoration : underline; +} +a:active { +color: #999999; +text-decoration : none; +}*/ + +.modify{ +text-decoration: none; +color: #FFFFFF; +font-weight: bold; +font-size: 14pt; +} + +.modify:hover{ +text-decoration: underline; +} + +/* Outer layer */ + +#outer { +text-align: left; +background: #ffffff; +color: inherit; +/**border: 1px solid #000000;**/ +margin: 0 auto; +padding: 0; +} + +/* Header */ + +.header { +background: #fcce32; +} + +.logolink { +padding: 6px 3px; +} + + .logolink a { +background: inherit; +} + +.hdr-links { +width: 230px; +text-align: center; +font-size : 10px; +padding : 0; +margin: 0; +} + +a.hdrnav:link { +color: #000099; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +} +a.hdrnav:visited { +color: #000099; +background : inherit; +text-decoration : none; +} +a.hdrnav:hover { +color: #000; +background : #fff; +text-decoration : underline; +} +a.hdrnav:active { +color: #999; +background : inherit; +text-decoration : none; +} + +.hdr-links .access { +text-decoration : underline; +color : #000; +background : inherit; +} + + +/* Top Nav Bar */ + +.top-nav { +height: 25px; +background: #c5c6ac; +color: inherit; +border: solid #000000; +border-width: 1px 0; +} +.top-nav-inner { +padding: 5px 10px; +} +.top-nav-inner a:link { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: none; +} +.top-nav-inner a:visited { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: none; +} +.top-nav-inner a:hover { +background: inherit; +color: #555555; +font-weight: bold; +text-decoration: underline; +} +.top-nav-inner a:active { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: underline; +} + + + +/* Image Bar */ + +.image-bar { +clear:both; +} + +.title-img { +border: 1px solid #000; +color: #ffffff; +background: #343234; +padding: 0 0 0 10px; +} + +.section-title { +font-size: 16px; +font-weight: bold; +font-family: verdana, arial, tahoma, sans-serif; +} + +.graphic-img { +border: 1px solid #000000; +} + +.blank-img { +border: 1px solid #000000; +background: #fcce32; +} + +.blank-img-ads { +border: 1px solid #000000; +background: #fcce32; +} + + +/* Content Section */ + +/*.content { +padding: 0 4px 4px 4px; +margin: 0; +}*/ + +.left-col { +border: 1px solid #000000; +background: #fcce32; +width: 145px; +} + +.left-nav { +margin: 3px; +} + +.statenav { + margin: 4px; +} + +.lnav-hdr { +background: #c5c6ac; +color: #000000; +font-family: verdana, tahoma, arial, sans-serif; +font-size: 11px; +font-weight: bold; +border: 1px solid #000000; +margin-top: 16px; +padding: 2px 5px; +} + +.left-nav p { +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +line-height: 14px; +margin:5px; +} + +.left-nav .access { +text-decoration : underline; +color : #000000; +background : inherit; +} + +a.lhdr:link { +color: #000000; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +font-weight: bold; +} +a.lhdr:visited { +color: #000000; +background : inherit; +text-decoration : none; +font-size: 10px; +font-weight: bold; +} +a.lhdr:hover { +color: #000; +background : #fcfefc; +text-decoration : underline; +} +a.lhdr:active { +color: #555; +background : inherit; +text-decoration : none; +} + +input.nobutton { + background-color: #0A50A1; + font-weight: bold; + font-size: 9px; + color: white; +} + +input.nobutton:visited { + background-color: #fcce32; + font-weight: bold; + font-size: 9px; + color: white; +} + +input.nobutton:active { + background-color: #fcce32 + color: white; + font-weight: bold; + font-size: 9px; +} + +a.nav:link { +color: #000099; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +} +a.nav:visited { +color: #000099; +background : inherit; +text-decoration : none; +} +a.nav:hover { +color: #000; +background : #fcfefc; +text-decoration : underline; +} +a.nav:active { +color: #999; +background : inherit; +text-decoration : none; +} + + + +/* Main content box */ +.content-box { +padding: 0; +margin: 0; +} + +/* right content box */ +.rhcol { +float: right; +width: 160px; +margin: 0 0 10px 15px; +background: #fcfefc; +color: #000000; +} +.rhcont { +border-left: 1px solid #000000; +border-bottom:3px solid #000000; +} +.rnav-hdr { +background: #c5c6ac; +color: #000000; +font-family: verdana, tahoma, arial, sans-serif; +font-size: 12px; +font-weight:bold; +border-bottom: 1px solid #000; +border-left: 2px solid #000000; +margin: 3px 0; +padding: 2px; +} + +/* main content area */ + +.content-title { +margin: 5px 0px 15px; +font-size: 14px; +text-decoration: underline; +} + +.bottomindent { +margin-bottom: 5px; +} + +.content-title-blog { +margin: 5px 0px 2px; +margin-left: 8px; +font-size: 16px; +text-decoration: none; +} + +.content-title-no1 { +margin: 5px 0px 15px; +font-size: 14px; +text-decoration: none; +} + +.content-title-mod { +margin: 5px 10px 15px; +font-size: 14px; +text-decoration: underline; +} + +.content-title-no { +margin: 0px 0px 0px; +font-size: 12px; +text-decoration: none; +} + +.content-box .cont { +margin: 10px 10px 15px 10px; +font-size: 11px; +line-height: 15px; +} + +.content-box .cont-list { +margin: 2px 10px 5px 10px; +} + +.content-box .sub-hdr { +margin: 2px 10px 10px; +font-size: 12px; +text-decoration: underline; +} + +.content-box .sub-hdr-mod { +margin: 2px 10px 10px; +font-size: 11px; +text-decoration: none; +} + +/* Bottom footer box */ + +.footer-col { +margin: 0 0 0 4px; +} + +.footer-box { +border: 1px solid #000000; +background:#343234; +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +color:#ffffff; +padding: 0 0 0 15px; +} + +.footer-box .cont1 { +margin: 8px 10px 5px 5px; +font-size: 11px; +line-height: 15px; +} +.footer-box .cont2 { +margin: 2px 10px 15px 5px; +font-size: 11px; +line-height: 15px; +} + +.footer-box input { +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +} + +/* Copyright layer */ + +.copyright { +background: #c5c6ac; +font-family: verdana, arial, tahoma, sans-serif; +font-size: 10px; +font-weight: bold; +padding: 6px; +border: solid #000000; +border-width: 1px 0 0; +} + +.rh-ad { +padding: 10px 0; +} + +/* Terms of use on front page */ +.tou { +margin: 0 0 0 10px; +} + + +/* for templates info */ +.temp-lh { +float : left; +width : 100px; +margin : 0; +padding : 2px 10px 2px 0; +text-align : left; +} +.temp-rh { +margin : 0; +padding : 2px 0; +text-align : left; +} +.date { +font-size : 10px; +color : #787d63; +background : inherit; +} +.temp-list ul { +font-size : 11px; +margin : 0; +padding : 0; +list-style-type : none; +} +.temp-list { +padding : 0 0 0 15px; +} + +/* small*/ +small.fsize { +font-size : 9px; +color : #999999; +background : inherit; +} + +.code { +color : #663300; +background : #eeeeee; +font-family : verdana, arial, sans-serif; +font-size : 11px; +line-height : 20px; +padding : 4px 10px; +margin : 10px 20px; +border : 1px dashed #999999; +} + + +/* Search form on front page */ + +.search-form { +color: #000; +background: #c5c6ac; +padding: 5px; +margin: 0px 0 10px 30px; +border: 1px solid #333333; +width:465px; +text-align: center; +white-space: nowrap; +} + +/* Search forms on Search page */ + +.adv-search { +margin: 0 0 0 15px; +} + +/* Tool Tip */ +.tip { +cursor : help; +border-bottom : 1px dashed #555555; +} + +/* ADVERTISEMENTS */ +/* container div is 'id=adspace' */ +#adspace { +background: inherit; +} +.ads { +background: inherit; +margin: 5px 0 15px 4px; +font-size: 11px; +padding: 5px; +border: solid #555; +border-width: 1px 0 0 1px; +} +.ads a:link { font-size: 11px; } diff --git a/site/forum.slowtwitch.com/www/forumfaq.html b/site/forum.slowtwitch.com/www/forumfaq.html new file mode 100644 index 0000000..69f1e7d --- /dev/null +++ b/site/forum.slowtwitch.com/www/forumfaq.html @@ -0,0 +1,74 @@ + + + + + + + + forumfaq + + + +
+
+
+
+ SLOWTWITCH FORUM FAQ + +

IS THIS A PUBLIC FORUM?
+ IS THIS A MODERATED FORUM?
+ EXPECTATION OF ANONYMITY

+ WHAT CAN I POST HERE?
+ WHAT CAN'T I POST HERE?
+ WHAT IS CONSIDERED ABUSIVE?
+ WHAT IF MY POST IS PULLED?
+ WHAT IF I'M BANNED?
+ MULTIPLE USER NAMES
+ COMPLAINTS ABOUT RETAILERS OR RACES

+ SCIENTIFIC DATA
+ ARE THESE ALL THE RULES?
+ DISCLAIMER

+
+

IS THIS A PUBLIC FORUM? No, this is a private forum open to the public in general, but with certain restrictions including but not limited to those listed herein. Using this forum is a privilege, not a protected right, and this privilege can be revoked at our sole discretion.
+
+ IS THIS A MODERATED FORUM? Yes, but moderation occurs after the fact, that is, we don't monitor a post before it is published, you publish it and then we eventually read it (or not). This forum receives between 1500 and 2500 posts every day, on average, too many posts to read and monitor. Accordingly, anything you see that you consider abusive or in bad taste; if you read a post that you think ought to be brought to our attention; please apprise us of this. In such case, the editorial email prompt is at the bottom of every page of the Slowtwitch.com editorial website (www.slowtwitch.com). That email address to use for such concerns is: slowman@slowtwitch.com
+
+ EXPECTATION OF ANONYMITY: You have none. There is none. Slowtwitch.com does not warrant that your anonymity will be protected, nor that we will protect it. If the preservation of your anonymity is vital to you, don't post on any Slowtwitch forum.
+
+ WHAT CAN I POST HERE? Anything associated with swim, bike, run, or triathlon in general. Off-topic posts belong in the Lavender Room. Notices belong in Notices, jobs sought or advertised in Jobs, items for sale in Classifieds. There are rules for each of these forums posted in "sticky" threads on the tops of each of those forums.
+
+ WHAT CAN'T I POST HERE? We are especially cranky about trial-by-internet. Do not register to post on this forum only to have your first post be a complaint against a company doing business in the sport of triathlon. Do not lodge a complaint against a named entity unless you yourself are willing to be identifiable, that is, no anonymous rants. Do not EVER say somebody's product is unsafe unless you have rock-solid demonstrable proof. This is as much for your benefit as anyone's (we'd prefer your hot temper doesn't result in a lawsuit a manufacturer lodges against you). Do not accuse anyone of taking performance enhancing drugs.
+
+ WHAT IS CONSIDERED ABUSIVE? We don't know, but we know it when we see it.
+
+ WHAT IF MY POST IS PULLED? You might find that something you posted is no longer on the forum on which it was originally posted. In all likelihood, you posted something that really belongs in either our Classifieds or Lavender Room forum, and you might find your post there (we'll just move your post or thread to the right forum). If your post is pulled entirely, you wrote something inappropriate—at least in our view—and we pulled the post.
+
+ WHAT IF I'M BANNED? Then you *might* have been (according to us) a very bad boy (or girl). We reserve the right to do this, and at our sole discretion. If you want to keep your account active, be civil. But you might be posting from a location inside a bloc of IP addresses we banned in order to weed out a spammer or bad actor. Email us privately and we'll find out of that's the case.
+
+ MULTIPLE USER NAMES: A big no-no. Stick to one user account. You can change your user name, but no creating multiple accounts. Note that your string of previous user names appends to your user profile.
+
+ COMMENTS ABOUT RETAILERS OR RACES: Neither complaints nor praises belong on the reader forum. They should be lodged as "comments" attached to the profiles of these business entities, which reside on our databases listing them. You'll find links to these databases on a blue bar across the very top of our reader forum pages. Likewise bike fitters, coaches, and tri clubs.
+
+ SCIENTIFIC DATA: If the data is merely a reprint from another website or publication, all that is needed is a link or a reference to the original source (and as much information is provided by the original publisher). If the data is coming from you, as a private individual, simply do your best to explain your testing protocol and methodology. You should be prepared to answer questions to the best of your ability. If the data is coming from a company that you work for or are otherwise affiliated with, you need to explain your affiliation to that company.
+
+

+
+

ARE THESE ALL THE RULES? No. But these are most of them, in broad strokes. There may be others we place here from time to time. As you become familiar with this forum, and this community, you'll see that we interfere only when we need to. If you take a deep breath before you post, and give your idea a second thought before you push the "send" button, you probably won't write anything with which we'll take umbrage.
+
+ DISCLAIMER: This is a very loosely moderated forum. We do not and cannot see everything written here, and we may not see an offensive post for awhile or indeed at all unless it is brought to our attention. We are hosting a global digital "water cooler," around which triathletes gather to "talk shop." As such we cannot and do not accept any liability for what others post here. We can simply make our best effort to monitor abuse that is brought to our attention.

+
+
+

Slowtwitch, Inc.
+ slowman@slowtwitch.com
+
661-944-5239
+
P.O. Box 56
+
17116 Bob's Gap Road
+ Valyermo, CA 93563-0056

+
+
+
+
+

+ + + \ No newline at end of file diff --git a/site/forum.slowtwitch.com/www/maintenance.html b/site/forum.slowtwitch.com/www/maintenance.html new file mode 100644 index 0000000..5357188 --- /dev/null +++ b/site/forum.slowtwitch.com/www/maintenance.html @@ -0,0 +1,31 @@ + + + forum.slowtwitch.com down for maintenance + + + + +

 

+ +
+
+ + + + + + + +
Under Construction
+

  +

forum.slowtwitch.com

+

We are down for maintenance and will be back in 15 minutues.
Sorry for any inconvenience.

+

 

+ +
+
+ + + + + diff --git a/site/forum.slowtwitch.com/www/static/admin/dashboard.css b/site/forum.slowtwitch.com/www/static/admin/dashboard.css new file mode 100644 index 0000000..7953ca8 --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/admin/dashboard.css @@ -0,0 +1,108 @@ +/******************/ +/* widget classes */ +/******************/ +.widget_column { + width: 320px; + min-height: 10px; + margin-right: 10px; + float: left; +} +#dashboard_row0 { + margin-top: 10px; +} +.widget_row { + width: 650px; + min-height: 10px; +} +.widget_column .widget { + width: 320px; +} +.widget_row .widget { + width: 650px; +} +.widget { + margin-bottom: 10px; + overflow: hidden; +} +.widget_heading { + cursor: move; +} +.widget_hover { + border: 1px dashed #AAAAAA; +} +.hidden { + display: none; +} +.widget table { + border-spacing: 0px; + border: 0px; + border-collapse: collapse; + width: 100%; +} +.widget th { + color: white; + background: #959595; + padding: 3px; +} +.widget td { + padding: 3px; +} +.widget .td_odd { + background: #edf1cc; +} +.widget .td_even { + background: #d9ddbb; +} +.widget .important { + background: #5c7ccf; + color: #FFF; + font-weight: bold; +} +.widget .line { + padding: 3px 5px; +} +.widget .number { + font-weight: bold; +} +#widget_list { + width: 650px; + background: #eaeaec; + clear: both; +} +h3.widget_list_heading { + background: #d2d2d2; + padding: 5px; + margin: 0; +} +#widget_list_toggle { + background: #959595; + float: left; + padding: 3px 5px; +} +#widget_list_toggle a { + text-decoration: none; + color: #FFF; + font-weight: bold; +} +#widget_list_toggle * { + vertical-align: middle; +} +#widget_list_content { + padding: 5px; +} +.widget_add { + text-decoration: none; + color: #000; + line-height: 20px; +} +.widget_add img { + vertical-align: middle; +} +/***************************/ +/* widget options classes */ +/**************************/ +.widget_footer a { + float: right; + text-decoration: none; +} + diff --git a/site/forum.slowtwitch.com/www/static/admin/style.css b/site/forum.slowtwitch.com/www/static/admin/style.css new file mode 100644 index 0000000..809db24 --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/admin/style.css @@ -0,0 +1,605 @@ +/***************/ +/* base styles */ +/***************/ +body { + margin: 0; + padding: 0; + font: 12px/1.231 tahoma, geneva, verdana, sans-serif; +} + +h1, h2, h3, h4, h5, h6 { + margin: 1em 0; + padding: 0; +} +h1 { + font-size: 20px; +} +h2 { + font-size: 17px; +} +h3 { + font-size: 15px; +} +h4 { + font-size: 13px; +} +h5 { + font-size: 12px; +} +h6 { + font-size: 11px; +} + +ul, ol, dl, blockquote { + margin: 1em; + padding: 0; +} +ul, ol, dl { + margin-left: 2em; +} +li { + margin: 0; + padding: 0; +} +p { + margin: 1em 0; + padding: 0; +} + +img { + border: 0; +} +form { + margin: 0; +} + +.wrapper { + min-width: 980px; + background: #fff; +} + +/**********/ +/* header */ +/**********/ +.header { + position: relative; /* for site title (.header h1) */ +} +.header .logo { + margin-bottom: 1px; /* add a small separation between logo and nav */ +} + +.header h1 { + position: absolute; + top: 10px; + right: 10px; +} + +.header ul.nav { + margin: 0; + list-style: none; + background: #5d5b66; + clear: both; +} +.header ul.nav li { + float: left; + background: transparent url(images/betweennav.gif) bottom right no-repeat; +} + +.header ul.nav li a { + margin-right: 1px; + padding: 7px 9px 7px 10px; + display: block; + text-decoration: none; +} +.header ul.nav li.selected a { + background: #737180; +} +.header ul.nav li a:link { + color: #fff; +} +.header ul.nav li a:visited { + color: #fff; +} +.header ul.nav li a:hover { + color: #fff; + background: #737180; +} +.header ul.nav li a:active { + color: #fff; +} + +.header ul.nav li.nav_home { + border-right: 1px solid #fff; + background: none; +} +.header ul.nav li.nav_home_selected { + background: #383741; +} +.header ul.nav li.nav_home a { + margin-right: 0; +} + +.header ul.nav li.nav_help, .header ul.nav li.nav_forum { + float: right; + background: none; +} +.header ul.nav li.nav_help a, .header ul.nav li.nav_forum a { + margin-right: 0; +} + +/**********/ +/* footer */ +/**********/ +.footer { + padding: 7px; + border-top: 16px solid #5d5b66; + font-size: 11px; +} + +.footer ul { + margin: 0px; + list-style: none; +} +.footer ul li { + padding: 0px 5px; + border-right: 1px solid #878787; + float: left; +} +.footer ul li.last { + border-right: 0px; +} +.footer ul li a { + color: #707070; + text-decoration: none; +} + +.footer_version { + float: right; +} + +/*************/ +/* container */ +/*************/ +.container { + background: url(images/faux-sidebar.gif) top left repeat-y; +} + +/***********/ +/* sidebar */ +/***********/ +.sidebar { + width: 160px; + float: left; +} + +.sidebar h3 { + margin: 0; + padding: 7px 10px; + font-size: 12px; + color: #fff; + background: #959595; +} +.sidebar ul { + margin: 8px 10px 20px 10px; + list-style: none; + line-height: 1.4; +} +.sidebar li { + padding-left: 9px; + color: #444; + background: url(images/bullet-square-gray.gif) center left no-repeat; +} +.sidebar li.selected a { + font-weight: bold; +} +.sidebar li a:link { + color: #444; + text-decoration: none; +} +.sidebar li a:visited { + color: #444; + text-decoration: none; +} +.sidebar li a:hover { + color: #444; + text-decoration: underline; +} +.sidebar li a:active { + color: #444; +} + +/***********/ +/* content */ +/***********/ +.content { + padding: 15px 0 30px 20px; + width: 780px; /* 980px - 160 (sidebar) - 20 (left padding) - 20 (right padding) */ + float: left; +} +.content h2 { + margin: 0 0 10px 0; + font-size: 20px; +} + +/**************/ +/* ui classes */ +/**************/ + +/* box */ +h3.box_title { + padding: 4px 8px; + margin: 0; + color: #fff; + background: #5d5b66; +} +.box_content { + background: #edf1cc; +} +.box_controls { + float: right; + padding: 5px; +} +.box_padding { + padding: 5px; +} + + +#container { + background-color: #edf0cd; +} + +#container h2 { + display: block; + background-color: #5C5B65; + color: #EFEFEF; + font-size: large; + font-weight: bold; + padding: 4px 10px; + margin: 0px; +} + +.panel { + margin: 10px; +} + +.panel .search { + float: right; + display: inline; +} + +.panel .search label { + font-size: 90%; +} + +.panel .actions { + float: left; +} + +.panel .letter-header { + background: #959595; + margin: 0px; + margin-bottom: 2px; + padding: 0px; + text-align: center; +} + +.panel .letter-header a { + color: #ffffff; + text-decoration: none; + font-weight: bold; + font-size: 10pt; + width: 3.65%; + float: left; + padding: 5px 0px; +} + +.panel .letter-header a.highlight { + background: #5c5b66; +} + +.panel .user-count { + font-size: 80%; + color: #5c5b66; + margin: 15px 0px; + float: left; +} + +.panel .paging { + float: right; + margin: 10px 0px; +} + +.panel table.user-listing { + width: 100%; + border: none; + border-collapse: collapse; + background-color: #F8F8F9; +} + +.panel table.user-listing thead tr { + background-color: #cecedb; + font-weight: bold; +} + +.panel table.user-listing thead tr td { + border: 1px solid #b1adc6; +} + +.panel table.user-listing thead tr td.ascending { + background: url(images/ascending.gif) no-repeat 92% 50%; +} +.panel table.user-listing thead tr td.descending{ + background: url(images/descending.gif) no-repeat 92% 50%; +} + +.panel table.user-listing thead tr td span { + text-align: right; + vertical-align: bottom; +} + +.panel table.user-listing thead tr td a { + color: black; + text-decoration: none; + float: left; +} + +.panel table.user-listing thead tr td a:hover { + text-decoration: underline; +} + +.panel table.user-listing tbody tr.alt { + background-color: #e4e4eb; +} + +.panel table.user-listing tbody { + font-size: smaller; + border-bottom: 1px solid #b1adc6; +} + +.panel table.user-listing td { + padding: 7px; + border-left: 1px solid #b1adc6; + border-right: 1px solid #b1adc6; +} + +.panel table.user-listing td.justify { + text-align: center; +} + +.okay { + color: green; +} +.alert { + color: red; +} +.blue { + color: #3f8b98; +} +.orange { + color: #ff9900; +} + +input.submit-dark { + border: 1px solid #9795a2; + background: #5c5b66; + color: #FFF; + font-weight: bold; + text-transform: uppercase; + padding: 2px 5px; + margin: 5px 0px 5px; +} + +input.submit-light { + border: 0px; + background: #959595 url(images/plus.gif) center left no-repeat; + color: #ffffff; + font-weight: bold; + text-transform: capitalize; + padding: 2px 5px 2px 15px; + margin: 5px 5px 15px 0px; +} + +input.submit-small-dark { + border: 0; + background: #5c5b66; + color: #ffffff; + font-weight: normal; + font-size: 80%; + padding: 2px 5px; + margin: 0px 5px 10px 0; +} + +input.text, input.password, textarea { + border:1px solid #999999; + padding:1px 0 1px 2px; +} + +.ac_results { + padding: 0px; + border: 1px solid WindowFrame; + background-color: Window; + overflow: hidden; +} +.ac_results ul { + width: 100%; + list-style: none; + padding: 0; + margin: 0; +} +.ac_results li { + margin: 0px; + padding: 2px 5px; + cursor: pointer; + display: block; + width: 100%; + font: menu; + font-size: 12px; + overflow: hidden; +} +.ac_loading { + background-color: beige; +} +.ac_over { + background-color: Highlight; + color: HighlightText; +} + +/*******************/ +/* utility classes */ +/*******************/ +.clear_simple { + clear: both; +} + +.clear:after { + height: 0px; + clear: both; + display: block; + visibility: hidden; + content: "."; +} +.clear { + display: inline-block; +} +/* start non ie/mac css \*/ +* html .clear { + height: 1%; +} +.clear { + display: block; +} +/* end non ie/mac css */ + +/*-----------------------------*\ +|* password strength indicator *| +\*-----------------------------*/ +#pass-strength { + font-weight: bold; +} +.pass-strength-weak { + color: red; +} +.pass-strength-good { + color: blue; +} +.pass-strength-strong { + color: green; +} + + +/*---------------*\ +|* forum listing *| +\*---------------*/ +div.forum-list { + padding: 0px 15px; +} + +div.forum-list input.collapse, +div.forum-list input.expand { + background: #a7a6b4 no-repeat; + border: 0px; + color: #ffffff; + font-weight: normal; + font-size: 7pt; + padding: 2px 15px 0px 2px; + margin: 0px 0px 10px 0px; + float: right; +} + +div.forum-list input.collapse { + background-image: url(images/ascending-white.gif); + background-position: 94% 4px; +} + +div.forum-list input.expand { + background-image: url(images/descending-white.gif); + background-position: 94% 3px; +} + +div.forum-list .collapsible { + background-image: url(images/ascending-button.gif); + background-repeat: no-repeat; + background-position: 99% 50%; +} + +div.forum-list .expandable { + background-image: url(images/descending-button.gif); + background-repeat: no-repeat; + background-position: 99% 50%; +} + +div.forum-list a img { + float: right; + margin-top: -15px; + margin-right: -3px +} + +div.forum-list .category { + background-color: #d0cfdd; + border: 1px solid #a7a6b4; + border-bottom: 2px solid #a7a6b4; + font-size: 11pt; + font-weight: bold; + padding: 5px 10px; + margin: 0px; +} + +div.forum-list .sub-category { + background-color: #e4e9ef; + border-left: 1px solid #a7a6b4; + border-right: 1px solid #a7a6b4; + border-top: 1px solid #caced7; + border-bottom: 2px solid #caced7; + padding: 5px 10px 5px 35px; + font-size: 11pt; + font-weight: bold; +} + +div.forum-list * span a { + font-weight: normal; + font-size: 8pt; + text-decoration: none; + color: #777777; + vertical-align: center; +} + +div.forum-list .forum { + color: #48484a; + background: #ffffff url(images/forum.gif) no-repeat 5px 0px; + border-left: 1px solid #a7a6b4; + border-right: 1px solid #a7a6b4; + font-size: 10pt; + font-weight: bold; + padding: 4px 10px 4px 35px; + margin: 0px; +} + +div.sub-category-forums .forum { + background-image: url(images/forum.gif); + background-position: 30px 0px; + padding: 4px 10px 4px 60px; +} + +div.forum-forums .forum { + background-image: url(images/sub-forum.gif); + background-position: 8px 0px; + padding: 4px 10px 4px 60px; + font-weight: normal; +} + +div.sub-category-forums div.forum-forums .forum { + background-image: url(images/sub-forum.gif); + background-position: 33px 0px; + padding: 4px 10px 4px 85px; + font-weight: normal; +} + +div.forum-list * .alternate { + background-color: #e7e6ec; +} + +div.forum-list .bottom { + height: 1px; + background: #a7a6b4; + padding: 0px; +} diff --git a/site/forum.slowtwitch.com/www/static/admin/thickbox.css b/site/forum.slowtwitch.com/www/static/admin/thickbox.css new file mode 100644 index 0000000..1946fc2 --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/admin/thickbox.css @@ -0,0 +1,151 @@ +* { padding: 0; margin: 0; } + +#TB_window { + font: 12px Arial, Helvetica, sans-serif; + color: #333333; +} + +#TB_secondLine { + font: 10px Arial, Helvetica, sans-serif; + color: #666666; +} + +#TB_window a:link { color: #666666; } +#TB_window a:visited { color: #666666; } +#TB_window a:hover { color: #000; } +#TB_window a:active { color: #666666; } +#TB_window a:focus { color: #666666; } + +#TB_overlay { + position: fixed; + z-index: 100; + top: 0px; + left: 0px; + height: 100%; + width: 100%; +} + +.TB_overlayMacFFBGHack { background: url(macFFBgHack.png) repeat; } + +.TB_overlayBG { + background-color: #000; + filter: alpha(opacity=75); + -moz-opacity: 0.75; + opacity: 0.75; +} + +* html #TB_overlay { /* ie6 hack */ + position: absolute; + height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px'); +} + +#TB_window { + position: fixed; + background: #EDF1CC; + z-index: 102; + color: #000000; + display: none; + text-align: left; + top: 50%; + left: 50%; +} + +* html #TB_window { /* ie6 hack */ + position: absolute; + margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px'); +} + +#TB_window img#TB_Image { + display: block; + margin: 15px 0 0 15px; + border-right: 1px solid #ccc; + border-bottom: 1px solid #ccc; + border-top: 1px solid #666; + border-left: 1px solid #666; +} + +#TB_caption { + height: 25px; + padding: 7px 30px 10px 25px; + float: left; +} + +#TB_closeWindow { + height: 25px; + padding: 11px 25px 10px 0; + float: right; +} + +#TB_closeAjaxWindow { + text-align: right; + float: right; + padding: 4px 8px; +} + +#TB_ajaxWindowTitle { + float: left; + margin-bottom: 1px; + font-size: 15px; + color: #FFFFFF; + padding: 4px 8px; +} + +#TB_title { + background-color: #5D5B66; + height: 27px; + margin: 0; +} + +#TB_ajaxContent { + clear: both; + padding: 2px 15px 15px 15px; + overflow: auto; + text-align: left; + line-height: 1.4em; +} + +#TB_ajaxContent.TB_modal { padding: 15px; } +#TB_ajaxContent p { padding: 5px 0px 5px 0px; } + +#TB_load { + position: fixed; + display: none; + height: 13px; + width: 208px; + z-index: 103; + top: 50%; + left: 50%; + margin: -6px 0 0 -104px; /* -height/2 0 0 -width/2 */ +} + +* html #TB_load { /* ie6 hack */ + position: absolute; + margin-top: expression(0 - parseInt(this.offsetHeight / 2) + (TBWindowMargin = document.documentElement && document.documentElement.scrollTop || document.body.scrollTop) + 'px'); +} + +#TB_HideSelect { + z-index: 99; + position: fixed; + top: 0; + left: 0; + background-color: #fff; + border: none; + filter: alpha(opacity=0); + -moz-opacity: 0; + opacity: 0; + height: 100%; + width: 100%; +} + +* html #TB_HideSelect { /* ie6 hack */ + position: absolute; + height: expression(document.body.scrollHeight > document.body.offsetHeight ? document.body.scrollHeight : document.body.offsetHeight + 'px'); +} + +#TB_iframeContent { + clear: both; + border: none; + margin-bottom: -1px; + margin-top: 1px; + _margin-bottom: 1px; +} \ No newline at end of file diff --git a/site/forum.slowtwitch.com/www/static/css b/site/forum.slowtwitch.com/www/static/css new file mode 120000 index 0000000..aa508ad --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/css @@ -0,0 +1 @@ +/var/home/slowtwitch/site/common/static/css \ No newline at end of file diff --git a/site/forum.slowtwitch.com/www/static/editor.old/editor.css b/site/forum.slowtwitch.com/www/static/editor.old/editor.css new file mode 100644 index 0000000..02d13ac --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/editor.old/editor.css @@ -0,0 +1,81 @@ +/* + * ================================================================= + * HTML Editor - A WYSIWYG web based editor for IE5.5+ and Mozilla v1.4+ + * + * Website : http://gossamer-threads.com/ + * Support : http://gossamer-threads.com/scripts/support/ + * Revision : $Id: editor.css,v 1.1 2006/04/05 23:23:13 brewt Exp $ + * + * Copyright (c) 2005 Gossamer Threads Inc. All Rights Reserved. + * Redistribution in part or in whole strictly prohibited. Please + * see LICENSE file for full details. + * ================================================================= + */ + +#editor { + margin: 0px; + padding: 0px; +} +.toolbar { + border: 1px solid; + border-color: white gray gray white; + height: 26px; + heigh\t: 24px; + background-color: lightgrey; + overflow: hidden; +} +.button { + margin: 0px; + padding: 0px; + border: 1px solid lightgrey; + width: 25px; + widt\h: 23px; + height: 24px; + heigh\t: 22px; + float: left; +} +.button-pressed { + border-color: gray white white gray; + background-color: #e9e7e3; + background-position: 1px 1px; +} +.button-mouse-over { + border-color: white gray gray white; +} +.button-pressed-mouse-over { + background-color: lightgrey; +} +.button-disabled { + filter: alpha(opacity = 25); + opacity: 0.25; +/* safari fix - when opacity is set, the background gets offset by about 50%?!? */ + background-repeat: no-repeat; +} +.separator { + margin: 2px 3px; + border-left: 1px solid gray; + border-right: 1px solid white; + width: 0px; + height: 20px; + float: left; +} +#editable_iframe { + border: 0px; + width: 100%; +} +#editable_source { + margin: 0px; + padding: 0px; + border: 0px; + width: 100%; + visibility: hidden; + position: absolute; +/* this somehow fixes the focus problems in mozilla */ + left: 0px; +} +#editable_content { + margin: 5px; +} +#editable_content p { + margin: 0px; +} diff --git a/site/forum.slowtwitch.com/www/static/editor.old/editor_color.html b/site/forum.slowtwitch.com/www/static/editor.old/editor_color.html new file mode 100644 index 0000000..37ad71c --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/editor.old/editor_color.html @@ -0,0 +1,135 @@ + + + + Color + + + + + + +
+
+
+
Sample
+ +
+
+ + +
+
+ + diff --git a/site/forum.slowtwitch.com/www/static/editor.old/editor_dialog.css b/site/forum.slowtwitch.com/www/static/editor.old/editor_dialog.css new file mode 100644 index 0000000..2129385 --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/editor.old/editor_dialog.css @@ -0,0 +1,193 @@ +/* + * ================================================================= + * HTML Editor - A WYSIWYG web based editor for IE5.5+ and Mozilla v1.4+ + * + * Website : http://gossamer-threads.com/ + * Support : http://gossamer-threads.com/scripts/support/ + * Revision : $Id: editor_dialog.css,v 1.2 2006/07/16 19:13:52 jagerman Exp $ + * + * Copyright (c) 2005 Gossamer Threads Inc. All Rights Reserved. + * Redistribution in part or in whole strictly prohibited. Please + * see LICENSE file for full details. + * ================================================================= + */ + +body, input, select { + font-family: "Microsoft Sans Serif", Tahoma, Arial, sans-serif; + font-size: 11px; +} +body { + margin: 10px; + background-color: #dcdad5; +} +form { + margin: 0px; +} +fieldset { + padding: 0px; +} +.fieldset { + padding: 5px 10px 0px 10px; +} +.buttons { + text-align: right; +} +.buttons input.submit, .buttons input.button { + margin: 2px 0px; + width: 75px; + height: 24px; +} +input.text { +/* ie6 fix to correct the size of the text input */ + padding: 1px 0px; +} + +#editor_font #form { + width: 335px; + float: left; +} +#editor_font #fontwrapper { + margin-right: 10px; + width: 150px; + float: left; +/* ie6 fix to prevent the input/select from expanding the box (some mystery + padding on the right side) */ + overflow: hidden; +} +#editor_font #stylewrapper { + margin-right: 10px; + width: 110px; + float: left; + overflow: hidden; +} +#editor_font #sizewrapper { + width: 55px; + float: left; + overflow: hidden; +} +#editor_font #font-sel, #editor_font #style-sel, #editor_font #size-sel, #editor_font #font, #editor_font #style, #editor_font #size { + width: 100%; +} +#editor_font #font-sel { + widt\h: 146px; +} +#editor_font #style-sel { + widt\h: 106px; +} +#editor_font #size-sel { + widt\h: 51px; +} +#editor_font #effectswrapper { + margin: 15px 10px 0px 0px; + width: 150px; + widt\h: 146px; + height: 82px; + heigh\t: 78px; + float: left; +} +#editor_font #samplewrapper { + margin: 15px 0px 0px 0px; + width: 175px; + widt\h: 171px; + height: 82px; + heigh\t: 78px; + float: left; +} +#editor_font #samplewrapper .fieldset { +/* ie5.5 needs this to center the sample */ + text-align: center; +} +#editor_font #sample { + margin: 0px auto; + border: 1px inset; + width: 147px; + widt\h: 145px; + height: 52px; + heigh\t: 50px; + overflow: hidden; + text-align: center; +} +#editor_font .buttons { + padding-top: 1em; +} + +#editor_link #urlwrapper { + width: 274px; + widt\h: 270px; + height: 78px; + heigh\t: 74px; + float: left; +} +#editor_link #url { + width: 200px; +} +#editor_link label.name { + margin-top: 2px; + width: 40px; + float: left; +} +#editor_link .row { + padding: 3px 0px; +} + +#editor_image #form { + width: 320px; + float: left; +} +#editor_image .row { + padding: 3px 0px; +} +#editor_image label.name { + margin-top: 2px; + width: 95px; + float: left; +} +#editor_image input.text#src-url-input { + width: 215px; +} +#editor_image label, #editor_image input.radio { + vertical-align: middle; +} + +#editor_color #palette div { + margin: 0px 1px 1px 0px; + border: 1px solid gray; + width: 25px; + widt\h: 23px; + height: 20px; + heigh\t: 18px; + font-size: 1px; + float: left; +} +#editor_color #palette div:hover, #editor_color #palette .selected { + border: 2px dotted white; + widt\h: 21px; + heigh\t: 16px; +} +#editor_color #samplewrapper { + margin-top: 10px; + float: left; +} +#editor_color #sample { + margin-right: 5px; + padding-top: 4px; + border: 1px solid #000000; + width: 80px; + widt\h: 78px; + height: 24px; + heigh\t: 18px; + float: left; + font-size: 11px; + text-align: center; +} +#editor_color #samplewrapper input { + width: 80px; +} +#editor_color .buttons { + margin-top: 8px; + float: right; +} + +label, input { + vertical-align: bottom; +} diff --git a/site/forum.slowtwitch.com/www/static/editor.old/editor_editable.html b/site/forum.slowtwitch.com/www/static/editor.old/editor_editable.html new file mode 100644 index 0000000..d95428a --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/editor.old/editor_editable.html @@ -0,0 +1,7 @@ + + + HTML Editor + + + + diff --git a/site/forum.slowtwitch.com/www/static/editor.old/editor_font.html b/site/forum.slowtwitch.com/www/static/editor.old/editor_font.html new file mode 100644 index 0000000..ec662c2 --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/editor.old/editor_font.html @@ -0,0 +1,251 @@ + + + + Font + + + + + + +
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+ Effects +
+
+
+ +
+
+
+ Sample +
+
+ AaBbYyZz +
+
+
+
+
+ + +
+
+ + diff --git a/site/forum.slowtwitch.com/www/static/editor.old/editor_iframe.html b/site/forum.slowtwitch.com/www/static/editor.old/editor_iframe.html new file mode 100644 index 0000000..2b07e15 --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/editor.old/editor_iframe.html @@ -0,0 +1,26 @@ + + + + HTML Editor + + + + +
+ + + diff --git a/site/forum.slowtwitch.com/www/static/editor.old/editor_link.html b/site/forum.slowtwitch.com/www/static/editor.old/editor_link.html new file mode 100644 index 0000000..d94f706 --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/editor.old/editor_link.html @@ -0,0 +1,59 @@ + + + + Create Link + + + + + +
+
+ Hyperlink Information +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + diff --git a/site/forum.slowtwitch.com/www/static/editor/editor.css b/site/forum.slowtwitch.com/www/static/editor/editor.css new file mode 100644 index 0000000..02d13ac --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/editor/editor.css @@ -0,0 +1,81 @@ +/* + * ================================================================= + * HTML Editor - A WYSIWYG web based editor for IE5.5+ and Mozilla v1.4+ + * + * Website : http://gossamer-threads.com/ + * Support : http://gossamer-threads.com/scripts/support/ + * Revision : $Id: editor.css,v 1.1 2006/04/05 23:23:13 brewt Exp $ + * + * Copyright (c) 2005 Gossamer Threads Inc. All Rights Reserved. + * Redistribution in part or in whole strictly prohibited. Please + * see LICENSE file for full details. + * ================================================================= + */ + +#editor { + margin: 0px; + padding: 0px; +} +.toolbar { + border: 1px solid; + border-color: white gray gray white; + height: 26px; + heigh\t: 24px; + background-color: lightgrey; + overflow: hidden; +} +.button { + margin: 0px; + padding: 0px; + border: 1px solid lightgrey; + width: 25px; + widt\h: 23px; + height: 24px; + heigh\t: 22px; + float: left; +} +.button-pressed { + border-color: gray white white gray; + background-color: #e9e7e3; + background-position: 1px 1px; +} +.button-mouse-over { + border-color: white gray gray white; +} +.button-pressed-mouse-over { + background-color: lightgrey; +} +.button-disabled { + filter: alpha(opacity = 25); + opacity: 0.25; +/* safari fix - when opacity is set, the background gets offset by about 50%?!? */ + background-repeat: no-repeat; +} +.separator { + margin: 2px 3px; + border-left: 1px solid gray; + border-right: 1px solid white; + width: 0px; + height: 20px; + float: left; +} +#editable_iframe { + border: 0px; + width: 100%; +} +#editable_source { + margin: 0px; + padding: 0px; + border: 0px; + width: 100%; + visibility: hidden; + position: absolute; +/* this somehow fixes the focus problems in mozilla */ + left: 0px; +} +#editable_content { + margin: 5px; +} +#editable_content p { + margin: 0px; +} diff --git a/site/forum.slowtwitch.com/www/static/editor/editor_color.html b/site/forum.slowtwitch.com/www/static/editor/editor_color.html new file mode 100644 index 0000000..d6ce09d --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/editor/editor_color.html @@ -0,0 +1,135 @@ + + + + Color + + + + + + +
+
+
+
Sample
+ +
+
+ + +
+
+ + diff --git a/site/forum.slowtwitch.com/www/static/editor/editor_dialog.css b/site/forum.slowtwitch.com/www/static/editor/editor_dialog.css new file mode 100644 index 0000000..2129385 --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/editor/editor_dialog.css @@ -0,0 +1,193 @@ +/* + * ================================================================= + * HTML Editor - A WYSIWYG web based editor for IE5.5+ and Mozilla v1.4+ + * + * Website : http://gossamer-threads.com/ + * Support : http://gossamer-threads.com/scripts/support/ + * Revision : $Id: editor_dialog.css,v 1.2 2006/07/16 19:13:52 jagerman Exp $ + * + * Copyright (c) 2005 Gossamer Threads Inc. All Rights Reserved. + * Redistribution in part or in whole strictly prohibited. Please + * see LICENSE file for full details. + * ================================================================= + */ + +body, input, select { + font-family: "Microsoft Sans Serif", Tahoma, Arial, sans-serif; + font-size: 11px; +} +body { + margin: 10px; + background-color: #dcdad5; +} +form { + margin: 0px; +} +fieldset { + padding: 0px; +} +.fieldset { + padding: 5px 10px 0px 10px; +} +.buttons { + text-align: right; +} +.buttons input.submit, .buttons input.button { + margin: 2px 0px; + width: 75px; + height: 24px; +} +input.text { +/* ie6 fix to correct the size of the text input */ + padding: 1px 0px; +} + +#editor_font #form { + width: 335px; + float: left; +} +#editor_font #fontwrapper { + margin-right: 10px; + width: 150px; + float: left; +/* ie6 fix to prevent the input/select from expanding the box (some mystery + padding on the right side) */ + overflow: hidden; +} +#editor_font #stylewrapper { + margin-right: 10px; + width: 110px; + float: left; + overflow: hidden; +} +#editor_font #sizewrapper { + width: 55px; + float: left; + overflow: hidden; +} +#editor_font #font-sel, #editor_font #style-sel, #editor_font #size-sel, #editor_font #font, #editor_font #style, #editor_font #size { + width: 100%; +} +#editor_font #font-sel { + widt\h: 146px; +} +#editor_font #style-sel { + widt\h: 106px; +} +#editor_font #size-sel { + widt\h: 51px; +} +#editor_font #effectswrapper { + margin: 15px 10px 0px 0px; + width: 150px; + widt\h: 146px; + height: 82px; + heigh\t: 78px; + float: left; +} +#editor_font #samplewrapper { + margin: 15px 0px 0px 0px; + width: 175px; + widt\h: 171px; + height: 82px; + heigh\t: 78px; + float: left; +} +#editor_font #samplewrapper .fieldset { +/* ie5.5 needs this to center the sample */ + text-align: center; +} +#editor_font #sample { + margin: 0px auto; + border: 1px inset; + width: 147px; + widt\h: 145px; + height: 52px; + heigh\t: 50px; + overflow: hidden; + text-align: center; +} +#editor_font .buttons { + padding-top: 1em; +} + +#editor_link #urlwrapper { + width: 274px; + widt\h: 270px; + height: 78px; + heigh\t: 74px; + float: left; +} +#editor_link #url { + width: 200px; +} +#editor_link label.name { + margin-top: 2px; + width: 40px; + float: left; +} +#editor_link .row { + padding: 3px 0px; +} + +#editor_image #form { + width: 320px; + float: left; +} +#editor_image .row { + padding: 3px 0px; +} +#editor_image label.name { + margin-top: 2px; + width: 95px; + float: left; +} +#editor_image input.text#src-url-input { + width: 215px; +} +#editor_image label, #editor_image input.radio { + vertical-align: middle; +} + +#editor_color #palette div { + margin: 0px 1px 1px 0px; + border: 1px solid gray; + width: 25px; + widt\h: 23px; + height: 20px; + heigh\t: 18px; + font-size: 1px; + float: left; +} +#editor_color #palette div:hover, #editor_color #palette .selected { + border: 2px dotted white; + widt\h: 21px; + heigh\t: 16px; +} +#editor_color #samplewrapper { + margin-top: 10px; + float: left; +} +#editor_color #sample { + margin-right: 5px; + padding-top: 4px; + border: 1px solid #000000; + width: 80px; + widt\h: 78px; + height: 24px; + heigh\t: 18px; + float: left; + font-size: 11px; + text-align: center; +} +#editor_color #samplewrapper input { + width: 80px; +} +#editor_color .buttons { + margin-top: 8px; + float: right; +} + +label, input { + vertical-align: bottom; +} diff --git a/site/forum.slowtwitch.com/www/static/editor/editor_editable.html b/site/forum.slowtwitch.com/www/static/editor/editor_editable.html new file mode 100644 index 0000000..d95428a --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/editor/editor_editable.html @@ -0,0 +1,7 @@ + + + HTML Editor + + + + diff --git a/site/forum.slowtwitch.com/www/static/editor/editor_font.html b/site/forum.slowtwitch.com/www/static/editor/editor_font.html new file mode 100644 index 0000000..a12cb0f --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/editor/editor_font.html @@ -0,0 +1,251 @@ + + + + Font + + + + + + +
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+ Effects +
+
+
+ +
+
+
+ Sample +
+
+ AaBbYyZz +
+
+
+
+
+ + +
+
+ + diff --git a/site/forum.slowtwitch.com/www/static/editor/editor_iframe.html b/site/forum.slowtwitch.com/www/static/editor/editor_iframe.html new file mode 100644 index 0000000..2b07e15 --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/editor/editor_iframe.html @@ -0,0 +1,26 @@ + + + + HTML Editor + + + + +
+ + + diff --git a/site/forum.slowtwitch.com/www/static/editor/editor_image.html.old b/site/forum.slowtwitch.com/www/static/editor/editor_image.html.old new file mode 100644 index 0000000..8776d4f --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/editor/editor_image.html.old @@ -0,0 +1,212 @@ + +<%~set page_mode = $in.editor_type%><%if not page_mode or page_mode ne message%><%set page_mode = 'post'%><%endif%> + + + Insert Image + + + + + + +
+ <%hidden_form%> + + <%-- populated from parent window by init() --%> + +
+
+ + checked<%endunless%>> + <%~unless attachment_uploaded%> + checked<%endif%>> + <%~endunless%> + checked<%endunless%>> +
+
+ + +
+ <%~unless attachment_uploaded%> +
+ + + +
+ <%~endunless%> +
+ + +
<%-- populated by init() --%>
+
+
+
+ + +
+
+
+ + + diff --git a/site/forum.slowtwitch.com/www/static/editor/editor_link.html b/site/forum.slowtwitch.com/www/static/editor/editor_link.html new file mode 100644 index 0000000..d94f706 --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/editor/editor_link.html @@ -0,0 +1,59 @@ + + + + Create Link + + + + + +
+
+ Hyperlink Information +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + diff --git a/site/forum.slowtwitch.com/www/static/fileman/luna/cluetip.css b/site/forum.slowtwitch.com/www/static/fileman/luna/cluetip.css new file mode 100644 index 0000000..e73f0e6 --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/fileman/luna/cluetip.css @@ -0,0 +1,176 @@ +/* global */ +#cluetip-close img { + border: 0; +} +#cluetip-title { + overflow: hidden; +} +#cluetip-title #cluetip-close { + float: right; + position: relative; +} +#cluetip-waitimage { + width: 43px; + height: 11px; + position: absolute; +} +.cluetip-arrows { + display: none; + position: absolute; + top: 0; + left: -11px; + height: 22px; + width: 11px; + background-repeat: no-repeat; + background-position: 0 0; +} +#cluetip-extra { + display: none; +} +/*************************************** + =cluetipClass: 'default' +-------------------------------------- */ + +.cluetip-default { + margin: 0px; + padding: 0px; + background: #edf2cb; + border: 3px solid #3e3d45; +} +.cluetip-default #cluetip-outer { + position: relative; + margin: 0; + padding: 0; + background: #edf2cb; +} +.cluetip-default h3#cluetip-title { + margin: 0 0 0px; + font-size: 11px; + padding: 3px 5px 2px 5px; + background: #5d5b66; + color: #fff; + font-weight: bold; +} +.cluetip-default #cluetip-title a { + color: #d9d9c2; +} +.cluetip-default #cluetip-inner { + padding: 10px; +} +.cluetip-default div#cluetip-close { + text-align: right; + margin: 0 5px 5px; + color: #900; +} + +/*************************************** + =cluetipClass: 'jtip' +-------------------------------------- */ +.cluetip-jtip { + background-color: transparent; +} +.cluetip-jtip #cluetip-outer { + border: 2px solid #ccc; + position: relative; + background-color: #fff; +} + +.cluetip-jtip h3#cluetip-title { + margin: 0 0 5px; + padding: 2px 5px; + font-size: 16px; + font-weight: normal; + background-color: #ccc; + color: #333; +} +.cluetip-jtip #cluetip-inner { + padding: 0 5px 5px; + display: inline-block; +} +.cluetip-jtip div#cluetip-close { + text-align: right; + margin: 0 5px 5px; + color: #900; +} + +/*************************************** + =cluetipClass: 'rounded' +-------------------------------------- */ + +.cluetip-rounded { + background: transparent url(images/bl.gif) no-repeat 0 100%; + margin-top: 10px; + margin-left: 12px; +} + +.cluetip-rounded #cluetip-outer { + background: transparent url(images/tl.gif) no-repeat 0 0; + margin-top: -12px; +} + +.cluetip-rounded #cluetip-title { + background-color: transparent; + padding: 12px 12px 0; + margin: 0 -12px 0 0; + position: relative; +} +.cluetip-rounded #cluetip-extra { + position: absolute; + display: block; + background: transparent url(images/tr.gif) no-repeat 100% 0; + top: 0; + right: 0; + width: 12px; + height: 30px; + margin: -12px -12px 0 0; +} +.cluetip-rounded #cluetip-inner { + padding: 5px 12px 12px; + margin: -18px -12px 0 0; + position: relative; +} + +.cluetip-rounded div#cluetip-close { + text-align: right; + margin: 0 5px 5px; + color: #009; + background: transparent; +} +.cluetip-rounded div#cluetip-close a { + color: #777; +} + +/* rounded arrows */ + +.clue-right-rounded .cluetip-arrows { + background-image: url(images/rarrowleft.gif); +} +.clue-left-rounded .cluetip-arrows { + background-image: url(images/rarrowright.gif); + left: 100%; + margin-left: 12px; +} +.clue-top-rounded .cluetip-arrows { + background-image: url(images/rarrowdown.gif); + top: 100%; + left: 50%; + margin-left: -11px; + height: 11px; + width: 22px; +} +.clue-bottom-rounded .cluetip-arrows { + background-image: url(images/rarrowup.gif); + top: -23px; + left: 50%; + margin-left: -11px; + height: 11px; + width: 22px; +} + + + +/* stupid IE6 HasLayout hack */ +.cluetip-rounded #cluetip-title, +.cluetip-rounded #cluetip-inner { + zoom: 1; +} diff --git a/site/forum.slowtwitch.com/www/static/fileman/luna/datePicker.css b/site/forum.slowtwitch.com/www/static/fileman/luna/datePicker.css new file mode 100644 index 0000000..1e39635 --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/fileman/luna/datePicker.css @@ -0,0 +1,115 @@ +table.jCalendar { + border: 1px solid #000; + background: #aaa; + border-collapse: separate; + border-spacing: 2px; +} +table.jCalendar th { + background: #333; + color: #fff; + font-weight: bold; + padding: 3px 5px; +} +table.jCalendar td { + background: #ccc; + color: #000; + padding: 3px 5px; + text-align: center; +} +table.jCalendar td.other-month { + background: #ddd; + color: #aaa; +} +table.jCalendar td.today { + background: #666; + color: #fff; +} +table.jCalendar td.selected { + background: #f66; + color: #fff; +} +table.jCalendar td.selected:hover { + background: #f33; + color: #fff; +} +table.jCalendar td:hover, table.jCalendar td.dp-hover { + background: #fff; + color: #000; +} +table.jCalendar td.disabled, table.jCalendar td.disabled:hover { + background: #bbb; + color: #888; +} + +/* For the popup */ + +/* NOTE - you will probably want to style a.dp-choose-date - see how I did it in demo.css */ + +div.dp-popup { + position: relative; + background: #ccc; + font-size: 10px; + font-family: arial, sans-serif; + padding: 2px; + width: 171px; + line-height: 1.2em; +} +div#dp-popup { + position: absolute; + z-index: 5000; +} +div.dp-popup h2 { + font-size: 12px; + text-align: center; + margin: 2px 0; + padding: 0; +} +a#dp-close { + font-size: 11px; + padding: 4px 0; + text-align: center; + display: block; +} +a#dp-close:hover { + text-decoration: underline; +} +div.dp-popup a { + color: #000; + text-decoration: none; + padding: 3px 2px 0; +} +div.dp-popup div.dp-nav-prev { + position: absolute; + top: 2px; + left: 4px; + width: 100px; +} +div.dp-popup div.dp-nav-prev a { + float: left; +} +/* Opera needs the rules to be this specific otherwise it doesn't change the cursor back to pointer after you have disabled and re-enabled a link */ +div.dp-popup div.dp-nav-prev a, div.dp-popup div.dp-nav-next a { + cursor: pointer; +} +div.dp-popup div.dp-nav-prev a.disabled, div.dp-popup div.dp-nav-next a.disabled { + cursor: default; +} +div.dp-popup div.dp-nav-next { + position: absolute; + top: 2px; + right: 4px; + width: 100px; +} +div.dp-popup div.dp-nav-next a { + float: right; +} +div.dp-popup a.disabled { + cursor: default; + color: #aaa; +} +div.dp-popup td { + cursor: pointer; +} +div.dp-popup td.disabled { + cursor: default; +} diff --git a/site/forum.slowtwitch.com/www/static/fileman/luna/luna.css b/site/forum.slowtwitch.com/www/static/fileman/luna/luna.css new file mode 100644 index 0000000..4bbaa7b --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/fileman/luna/luna.css @@ -0,0 +1,82 @@ +@import url(luna_core.css); + +/* +This file inherits from luna_core.css, so styles in this file will override +styles defined in it. Changes to this file will NOT be overwritten during +upgrades. + +Here's a quick tutorial on overriding styles. Say you want to change the +background colour of the body. Looking at luna_core.css, the colour is +defined in the rule: + + body { + margin: 0px; + padding: 0px; + color: #33332e; + background: #ffffff; + font: normal 11px tahoma, geneva, verdana, sans-serif; + } + +To change the background colour of white (#ffffff) to gray (#dddddd), you would +add the following to this file: + + body { + background: #dddddd; + } + +If you also wanted to change the font as well as the background, then you could +use: + + body { + background: #dddddd; + font: normal 12px times new roman; + } + +Also note that every template's body id is assigned the template name (without +the .htm extension). So if you want to change the h2 heading in the +home.htm template, you can do this by: + + #home h2 { + color: red; + font-weight: bold; + } + +To start you off, some sample overriding styles have been provided below. Note +that you will have to uncomment the rules for them to work. +*/ + +/* Logo size and image source */ +/* +#logo { + width: 250px; + height: 80px; + background-image: url(images/logo.gif); +} +*/ +/* If the height of your logo changes from the original, then you will probably +want to also change the vertical position of the login link */ +/* +#loginbar a { + margin-top: 25px; +} +*/ + +/* Change width of document */ +/* +#wrapper { + width: 750px; +} +*/ + +/* Don't want the shadows? */ +/* +.shadowtop, .shadowbottom, .shadowleft, .shadowright { + background: none; +} +.shadowtopleft, .shadowtopright, .shadowbottomleft, .shadowbottomright { + width: auto; + height: auto; + float: none; + background: none; +} +*/ diff --git a/site/forum.slowtwitch.com/www/static/fileman/luna/luna_core.css b/site/forum.slowtwitch.com/www/static/fileman/luna/luna_core.css new file mode 100644 index 0000000..effc111 --- /dev/null +++ b/site/forum.slowtwitch.com/www/static/fileman/luna/luna_core.css @@ -0,0 +1,1298 @@ +/* + * ================================================================= + * Gossamer Mail - enhanced email management system + * + * Website : http://gossamer-threads.com/ + * Support : http://gossamer-threads.com/scripts/support/ + * Revision : $Id: luna_core.css,v 1.37 2008/11/26 20:38:28 brewt Exp $ + * + * Copyright (c) 2005 Gossamer Threads Inc. All Rights Reserved. + * Redistribution in part or in whole strictly prohibited. Please + * see LICENSE file for full details. + * ================================================================= + */ + +/* +Do not make changes to this file. Any changes will be overwritten the next +time you upgrade Gossamer Mail. You can override any css in this file by +defining overriding styles in luna.css. +*/ + +/* +There are a few CSS filters/hacks used in this file to fix bugs in various +browsers. Here is a list of them: + +- Star html selector bug (css rule only applies to ie/win) + http://www.info.com.ph/~etan/w3pantheon/style/starhtmlbug.html + eg. * html {} +- Simplified box model hack (hide css from ie5-5.5/win) + http://www.doxdesk.com/personal/posts/css/20020212-bmh.html + eg. w\idth: 100px; +- Commented backslash (hide css from ie5/mac) + http://www.sam-i-am.com/work/sandbox/css/mac_ie5_hack.html +*/ + +/*------------------*\ +|* global interface *| +\*------------------*/ +body { + margin: 0px; + padding: 0px; + color: #33332e; + background: #ffffff; + font: normal 11px tahoma, geneva, verdana, sans-serif; + overflow: hidden; +} +img { + border: none; +} + +/* forms */ +form { + margin: 0px; +} +input, textarea, select { + font: normal 13px tahoma, geneva, verdana, sans-serif; +} +input.text, input.password, textarea { + padding: 1px 0px 1px 2px; + border: 1px solid #57594b; +} +input.checkbox { + margin: 4px 4px 4px 0px; +} +select { + border: 1px solid #57594b; +} + +/* links */ +a, a:visited { + color: #212126; +} +a:hover { + color: #5c5b66; + text-decoration: none; +} + +/* headings */ +h1.header { + font-size: 16px; +} +h2.header { + font-size: 15px; +} +h3.header { + font-size: 13px; + text-align: right; + padding: 0px 5px 10px 0px; + margin: 0px 0px 1px 0px; +} +h4.header { + font-size: 12px; +} +h5.header { + font-weight: normal; + font-size: 11px; +} +#wrapper { + margin: 0px; +} +.noauth #wrapper { + text-align: center; +} +/*-----------*\ +|* home page *| +\*-----------*/ +#home { + overflow: hidden; +} +/*--------*\ +|* header *| +\*--------*/ +#logo { + width: 205px; + height: 50px; + background: transparent url(images/logo.gif) top left no-repeat; + text-indent: -9999px; +} +#logo h1, #logo a { + margin: 0px; + width: 100%; + height: 100%; +} +#logo a { + text-decoration: none; + display: block; +} +#header { + background: transparent url(images/swirls.gif) bottom right no-repeat; + margin: 3px 0px; +} +#loginbar { + float: right; + text-align: right; + margin-right: 10px; +} +#loginbar a { + margin-top: 8px; + padding: 10px 30px 10px 0px; + color: #212126; + background: transparent url(images/logout.gif) center right no-repeat; + font-weight: bold; + display: block; +} +#loginbar a:hover { + color: #5c5b66; +} +.noauth #header { + background: transparent url(images/larger_swirls.gif) top right no-repeat; + border-bottom: 3px solid #383741; + text-align: left; +} +.noauth #logo { + width: 260px; + height: 80px; + background: transparent url(images/logo_large.gif) top left no-repeat; +} +/*------------*\ +|* navigation *| +\*------------*/ +.navbar { + position: relative; + margin-bottom: 1px; + height: 22px; + background: url(images/betweennav_hor.gif) #5c5b66 bottom left repeat-x; +} + +ul.primarynav { + position: absolute; + margin: 0px; + padding: 0px; + width: 100%; + list-style: none; +} +ul.primarynav li.navitem { + position: relative; + float: left; + list-style: none; + display: block; +} +ul.primarynav li.navitem a { + margin-right: 1px; + font-size: 11px; + padding: 4px 0; +} +ul.primarynav input.submit { + background: #5c5b66; + border: 0px; + color: #ffffff; + font-size: 11px; + margin-right: 1px; + padding: 2px 5px; + cursor: pointer; + cursor: hand; +} +ul.primarynav input.submit:hover { + background: #737180; +} +ul.primarynav input.selected { + background: #383741; +} +#primarynav li.navitem a { + color: #ffffff; + padding: 3px 8px; + display: block; + text-decoration: none; +} +#primarynav li.navitem a:visited { + color: #ffffff; +} +#primarynav li.navitem a.navlink:hover { + color: #ffffff; + background: #737180; +} +#primarynav li.navitem a.sublink:hover { + padding-bottom: 6px; +} +#primarynav li.selected a { + background: #383741; +} +#primarynav { + top: 57px; + left: 0px; +} +#primarynav li.navitem { + background: transparent url(images/betweennav.gif) bottom right no-repeat; +} +#breadcrumbsnav { + top: 84px; + left: 10px; + width: 90% +} +#breadcrumbsnav li.navitem ul { + top: 13px; +} +#breadcrumbsnav li.navitem ul li a, #breadcrumbsnav li.nav-item ul li a:visited { + padding-left: 5px; + color: #fff; + text-decoration: none; +} +#breadcrumbsnav li.navitem img { + margin: 2px 2px 0 0; +} +#breadcrumbsnav img, #breadcrumbsnav span { + vertical-align: middle; +} +a.root { + font-weight: bold; +} +/*--------------*\ +|* Toolbar menu *| +\*--------------*/ +ul.primarynav ul { + position: absolute; + top: 20px; + left: 0px; + width: 200px; + display: none; + background: #737180; + margin: 0px; + padding: 0px; + border: 1px solid #ccc; + z-index: 1000; +} +ul.primarynav iframe { + position: absolute; + width: 190px; + border: 0; + top: 20px; + left: 0px; + z-index: 500; +} + +ul.primarynav li.navitem:hover ul, ul.primarynav li.sfhover ul, ul.primarynav li.navitem:hover ul, ul.primarynav li.sfhover iframe { + display: block; + list-style: none; +} +ul.primarynav li.sfhover ul li { + padding: 0px; +} +ul.primarynav li.navitem:hover ul li hr, ul.primarynav li.sfhover ul li hr { + margin: 0px; + padding: 0px; + height: 1px; +} +ul.primarynav li.navitem:hover ul li a, ul.primarynav li.sfhover ul li a { + display: block; +} +ul.primarynav li.navitem:hover ul li a:hover, ul.primarynav li.sfhover ul li a:hover { + background: #a09fac; +} +/*-----------*\ +|* searchbar *| +\*-----------*/ +#cdbar { + position: relative; + margin-bottom: 3px; + padding: 3px 10px; + border-bottom: 1px solid #57594b; + background: #edf2cb; + height: 16px; +} +#cdbar #cdform { + position: absolute; + top: 3px; + right: 3px; + z-index: 100; +} +#cdbar input.text, #cdbar input.submit { + margin-right: 5px; + font-size: 10px; +} +#cdbar input.submit { + padding: 0px 3px; + height: 19px; + line-height: 1em; +} +/* these colours are the same as defined by 'a', but we unset a:visited */ +#cdbar { + padding: 5px 3px 5px 8px; +} +#cdbar a { + color: #212126; + font-size: 10px; +} +#cdbar a:hover { + color: #5c5b66; +} +#cdbar .username { + padding-top: 4px; + float: right; +} +/*--------*\ +|* footer *| +\*--------*/ +#footer { + margin-top: 3px; + padding: 5px; + border-top: 1px solid #57594b; + background: #edf2cb; +} +#footer img { + float: right; +} +#footer p { + margin: 0px; + padding: 10px 0px; + color: #8a8d77; + font-size: 9px; + text-align: left; +} + +/*---------------*\ +|* contentheader *| +\*---------------*/ +#wrapper-content { + position: relative; + background: #e2e1eb; + width: 100%; +} +#contentheader { + position: absolute; + padding: 7px 10px; + background: #e2e1eb; + height: 54px; + display: none; +} +#contentheader input, #contentheader select, #contentheader label { + vertical-align: middle; +} +#contentheader input, #contentheader select { + vertical-align: middle; +} +#contentheader .row { + padding: 0px; +} +#contentheader .row .value { + padding: 1px 0; +} +#contentheader .row label.name { + width: 50px; + padding: 2px 0px; +} +#contentheader .row .value select { + width: auto; + margin: 1px 0; +} +#contentheader .row .value label { + padding-right: 5px; +} +#contentheader .switch-mode { + float: right; + font-weight: bold; + display: none; +} +#contentheader .longrun { + padding-left: 25%; +} +#contentheader input.longtext { + width: 40%; +} +#cmd-upload input.file { + font-size: 11px; + border: 1px solid #828284; +} +.contentframe { + overflow-y: scroll; +} +.contentframe .preview-image { + padding: 5px 0; +} + +/*--------------------\ +|* Error and message *| +\*-------------------*/ +#contentmessage { + margin: 2px 8px 0 8px; + padding: 2px 2px 0 2px; + border: 1px solid #ffffff; + color: #ffffff; + font-size: 12px; + height: 16px; +} +.error { + background: #8c3030; +} +.message { + background: #3a5841; + vertical-align: middle; +} +.error *, .message * { + margin: 0px; + padding: 0px; +} +.noauth #contentmessage { + border: 0px; + margin: 0px auto; + padding: 0px; + width: 690px; + widt\h: 685px; +} +.noauth #contentmessage .error, .noauth #contentmessage .message { + border: 1px solid #fff; + margin: 0; +} +#reset_password .row label.name { + width: 60px; +} +/*---------------*\ +|* contentfooter *| +\*---------------*/ +#contentfooter { + background: #e2e1eb; + padding: 0px 10px 4px 10px; + font-size: 10px; +} +#contentfooter div.summary { + float: right; +} +#contentfooter span { + font-weight: bold; +} + +/*---------*\ +|* content *| +\*---------*/ +#ocwrapper { + position: absolute; + width: 100%; + left: 0; +/* background colour of left sidebar */ + border-left: 0px solid #e2e1eb; +/* background colour of right sidebar */ + border-right: 0px solid #e2e1eb; + background: #e2e1eb; +} +#contentwrapper { + width: 100%; + float: left; + position: relative; +} +#content { + margin: 0px 10px; + padding: 10px; + background: #ffffff; + text-align: left; +} +#content h1, #content h2 { + margin: 0px; + padding: 0px; +} +.noauth #content h2 { + color: #ffffff; + background: #383741; + text-transform: uppercase; + font-size: 11px; + padding: 3px 4px; + margin-top: 0px; +} +.noauth #content h3 { + font-size: 13px; + text-align: right; + padding-bottom: 5px; + margin: 10px 0px 1px 0px; + background: transparent url(images/row-separator.gif) repeat-x bottom left; +} +.noauth #content h4 { + font-size: 12px; +} +.noauth #content h5 { + font-weight: normal; + font-size: 11px; +} +.noauth #ocwrapper { + border: none; + position: relative; +} +.noauth #contentwrapper { + margin: 0px auto; + width: 700px; + float: none; +} +.noauth .lostpasswd { + float: right; + padding-top: 5px; +} + +/* hack for ie/win's guillotine bug */ +/* start non ie/mac css \*/ +* html #content { + height: 1%; +} +/* end non ie/mac css */ + +/*------------------------------*\ +|* actions and paging *| +\*------------------------------*/ +.toolbar { + padding: 5px; + border-top: 1px solid #ffffff; + border-bottom: 1px solid #ffffff; + background: #cac9d1; +} +.toolbar input.text { + width: 300px; +} +.toolbar input.short-text { + width: 100px; +} +.toolbar input, .toolbar select { + vertical-align: middle; +} +.toolbar select.action { + float: left; + margin-right: 15px; +} +.toolbar .action-form { + float: left; +} +.toolbar input.submit, .modal-dialog input.submit { + padding: 0px 5px; + font-size: 11px; + font-weight: bold; +} +#group div { + float: left; +} +#group span, #group label, #group input { + vertical-align: middle; +} +#group .label { + margin: 0 3px 0 10px; + line-height: 10px; + text-align: center; +} +#group .label span { + font-size: 9px; +} +#group input.text { + width: 35px; +} +.toolbar .group { + border: 1px solid #85817a; + padding: 0px 3px 3px 3px; + margin: 0px 1px 0 0; +} +.toolbar .group span { + background: #cac9d1; + font-size: 9px; + font-weight: bold; +} +.toolbar .group input.checkbox { + margin: 0px; + padding: 0px; + font-size: 9px; + line-height: 1px; + text-indent: 0px; +} +.paging { + float: right; +} +.paging img, .paging select { + vertical-align: middle; +} +/* paging style 3 rules */ +.paging a:visited { + color: #212126; +} +.paging span { + font-weight: bold; +} +.noauth .loginactions { + margin-top: -25px; + text-align: right; +} +.toolbar h3.header { + float: right; + margin: 0px; + padding: 0px; +} + +/*-----------------*\ +|* global elements *| +\*-----------------*/ +.shadowtop, .shadowbottom { + height: 10px; + font-size: 1px; + display: inline-block; +/* ie/mac fix \*/ + display: block; +/* end fix */ +} +.shadowtopleft, .shadowtopright, .shadowbottomleft, .shadowbottomright { + width: 15px; + height: 10px; + font-size: 1px; +} +.shadowtop { + background: transparent url(images/shadow-top.gif) bottom repeat-x; +} +.shadowbottom { + background: url(images/shadow-bottom.gif) top left repeat-x; +} +.shadowleft { + background: url(images/shadow-left.gif) left repeat-y; +} +.shadowright { + background: url(images/shadow-right.gif) right repeat-y; +} +.shadowtopleft { + float: left; + background: url(images/shadow-topleft.gif) bottom left no-repeat; +} +.shadowtopright { + float: right; + background: url(images/shadow-topright.gif) bottom right no-repeat; +} +.shadowbottomleft { + float: left; + background: url(images/shadow-bottomleft.gif) top left no-repeat; +} +.shadowbottomright { + float: right; + background: url(images/shadow-bottomright.gif) top right no-repeat; +} +.spacer { + background: transparent url(images/row-separator.gif) repeat-x bottom left; +} +.top-spacer { + background: transparent url(images/row-separator.gif) repeat-x top left; +} + +/* This is unfortunately needed because setting a background on a does + * *not* actually affect the tr in IE - instead it just puts that background on + * each , which means the image from one may not have the right + * horizontal offset as the next . To get around this, each spacer row + * contains just one, empty, . This sucks. Patches welcome. + */ +tr.spacer { + height: 3px; + background-position: center left; +} + +/* forms */ +.row .description { + background: #e4e4e8; + color: #212126; + padding: 4px 2px; +} +.row .description .warning { + color: #8c3030; +} +.row label.name { + margin: 0px; + padding: 4px 5px 4px 25px; + border: none; + width: 130px; + float: left; + vertical-align: middle; +} +.row label.name span { + color: #8c3030; +} +.row label.name small { + font-size: 9px; +} +.row .value { + float: left; + padding: 4px; +} +.row .value input.text, .row .value input.password { + width: 150px; + font-size: 11px; +} +#cmd-search input.longtext, .row .value input.longtext { + width: 300px; +} +.row .value select.wide { + width: 300px; +} +.row .value textarea { + width: 325px; + font-size: 11px; +} +.row .value input.text.shorttext { + width: 100px; +} +.row .value input.text.tinytext { + width: 30px; +} +.row .value input, .row .value label { + vertical-align: middle; +} +.row .value textarea.user-access { + width: 400px; + height: 60px; +} +.button, .submit { + font-size: 11px; +} + +.clear:after { + height: 0px; + clear: both; + display: block; + visibility: hidden; + content: "."; +} +.clear { + display: inline-block; +} +/* start non ie/mac css \*/ +* html .clear { + height: 1%; +} +.clear { + display: block; +} +/* end non ie/mac css */ +.hide { + display: none; +} +/* Define the widths for the folder table - with table-layout: fixed, only the + * first row's widths are taken into account, NOT the table contents. */ +/* clearing class */ +.foldertab { + border-collapse: collapse; + width: 100%; + font-size: 11px; + table-layout: fixed; +} +.foldertab pre { + padding: 0px; + margin: 0px; +} +.readme { + margin: 10px 0; +} +.readme h1 { + font-size: 12px; + padding: 0; + margin: 0px; +} +* html .foldertab { + top: 0px; left: 0px; right: 0px; bottom: 0px; + width: auto; +} +.foldertab th { + padding: 3px 2px; + color: white; + background: #383741; + font-size: 11px; + text-transform: uppercase; + text-align: left; +} +.foldertab th a, .foldertab th a:hover, .foldertab th a:visited { + display: block; + width: 100%; + height: 100%; + color: #fff; + text-decoration: none; +} +.foldertab .empty { + text-align: center; + vertical-align: middle; + height: 50px; +} +.foldertab .icon, .foldertab .selectbox { + width: 18px; + text-align: center; +} +.foldertab .scrollbar { + width: 12px; +} +.foldertab .name { } +.foldertab .size, .foldertab .owner { + width: 80px; +} +.foldertab .type { + width: 120px; +} +.foldertab .modified { + width: 140px; +} +.foldertab .permission, .foldertab .action { + width: 110px; +} +.foldertab .username { + width: 150px; +} +.foldertab .email { + width: 300px; +} +.foldertab td { + padding: 1px 2px 0px 2px; + text-align: left; +} +.foldertab tr.spacer { + height: 3px; +} +.foldertab tr.symlink td, .foldertab tr.symlink a { + color: #2c9490; +} +.command pre { + margin: 5px 0 10px 0; +} +.command .prompt { + font-weight: bold; +} +.command .error { + background: #fff; + color: #bd0000; +} +#command-dialog .error { + background: none; + color: #bd0000; +} +#command-dialog .message { + background: none; + color: #2e8b57; +} +#cmd-protect input.text { + width: 80px; +} +#cmd-protect .row label.name { + width: 120px; + padding-top: 3px; +} + +/* generic modal dialog style */ +.modal-dialog { + display: none; + position: fixed; + top: 17%; + left: 50%; + border: 3px solid #3e3d45; + + +/* ie6 - doesn't support fixed positioning */ + _position: absolute; + _top: expression((document.documentElement.scrollTop || document.body.scrollTop) + Math.round(17 * (document.documentElement.offsetHeight || document.body.clientHeight) / 100) + 'px'); +} +.jqmOverlay { + background: #ddd; +} + +/* ie6 - prevent ActiveX bleed-through ( + <%-- populated from parent window by init() --%> + +
+
+ + checked<%endunless%>> + <%~unless attachment_uploaded%> + checked<%endif%>> + <%~endunless%> + checked<%endunless%>> +
+
+ + +
+ <%~unless attachment_uploaded%> +
+ + + +
+ + + + +
+
+ +
+ <%~endunless%> +
+ + +
<%-- populated by init() --%>
+
+
+
+ + +
+ +
+ + +<%~unless attachment_uploaded%> + +<%~endunless%> + + \ No newline at end of file diff --git a/site/forum/editor_image_new.html b/site/forum/editor_image_new.html new file mode 100644 index 0000000..a6caed3 --- /dev/null +++ b/site/forum/editor_image_new.html @@ -0,0 +1,439 @@ + +<%~set page_mode = $in.editor_type%><%if not page_mode or page_mode ne message%><%set page_mode = 'post'%><%endif%> + + + Insert Image + + + + + + + +
+ <%hidden_form%> + + <%-- populated from parent window by init() --%> + +
+
+ + checked<%endunless%>> + <%~unless attachment_uploaded%> + checked<%endif%>> + <%~endunless%> + checked<%endunless%>> +
+
+ + +
+ <%~unless attachment_uploaded%> +
+ + + +
+ + + + +
+
+ +
+ <%~endunless%> +
+ + +
<%-- populated by init() --%>
+
+
+
+ + +
+
+
+ + +<%~unless attachment_uploaded%> + +<%~endunless%> + + diff --git a/site/forum/error.html b/site/forum/error.html new file mode 100644 index 0000000..708dfd2 --- /dev/null +++ b/site/forum/error.html @@ -0,0 +1,38 @@ + + + + <%cat_full_name%>: <%forum_name%><%if this_page > 1%>: Page <%this_page%><%endif%> +<%include include_common_head.html%> + + + +
+ <%include include_header.html%> + +
+
+ <%~set content_header_title = "An error occurred!"%> + + <%include include_breadcrumb.html%> + +
<%-- start content--%> +
    + <%~if error starts '
  • '%> + <%error%> + <%~elsif error%> +
  • <%error%>
  • + <%~endif%> + <%~loop error_loop%> +
  • <%loop_value%>
  • + <%~endloop%> +
+
+
+
+ + <%include include_footer.html%> +
+ + +<%~include include_global_js.html%> + diff --git a/site/forum/favourites_others.html b/site/forum/favourites_others.html new file mode 100644 index 0000000..48ba3b0 --- /dev/null +++ b/site/forum/favourites_others.html @@ -0,0 +1,43 @@ + + + + <%cat_full_name%>: Favourites +<%include include_common_head.html%> + + + +
+ <%include include_header.html%> + +
+ <%~set content_header_title = "Favourites"%> + + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $user_username%> + <%~set escaped_username = escape_url unescape_html $user_username%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "username=$escaped_username")%> + <%~set breadcrumbs.1.name = $content_header_title%> + +
+ + + <%include include_breadcrumb.html%> + +
<%-- start content--%> + <%include include_content_header.html%> +

+ Posts and threads written by <%include include_username.html%> that have been favourited by other users. +

+ <%include include_user_favourites.html%> + +
<%-- end content --%> +
+ +
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/forum/forum_ban.html b/site/forum/forum_ban.html new file mode 100644 index 0000000..5f0e990 --- /dev/null +++ b/site/forum/forum_ban.html @@ -0,0 +1,44 @@ + +<%~set search_widget = 1%> + + + <%cat_full_name%>: <%forum_name%>: <%if ban_ip%>IP<%else%>User<%endif%> Banned + <%include include_common_head.html%> + + +<%~if in.ban_type eq ban_ip%><%set content_header_title = 'IP Address'%><%else%><%set content_header_title = 'User'%><%endif%> +<%~set content_header_title .= ' Banned'%> + + +
+ <%include include_header.html%> + +
+
+ + + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + <%~set breadcrumbs.1.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
<%-- start content--%> + <%include include_content_header.html%> + +

+ <%if ban_ip%>IP '<%ban_ip%>'<%else%><%include include_username.html%><%endif%> has been banned from <%cat_full_name%>: <%forum_name%> + <%~if ban_expiry%> until <%ban_expiry_date%><%endif%>. +

+ +
<%-- end content --%> +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/forum/forum_ban_list.html b/site/forum/forum_ban_list.html new file mode 100644 index 0000000..571bc61 --- /dev/null +++ b/site/forum/forum_ban_list.html @@ -0,0 +1,102 @@ + +<%~set search_widget = 1%> + + + <%cat_full_name%>: <%forum_name%>: <%if ban_ip%>IP<%else%>User<%endif%> Banned + <%include include_common_head.html%> + + +<%~set content_header_title = "$forum_name: Bans"%> + +
+ <%include include_header.html%> + +
+
+ + + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + <%~set breadcrumbs.1.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
<%-- start content--%> + <%include include_content_header.html%> + + <%~if not num_bans%><%-- This cannot be bans.length because bans is a code ref --%> +

+ There are no bans are set on this forum. +

+ <%~else%> +
+ <%hidden_form%> + + +
+
+
+
Set By
+
Username
+
IP Address
+
Date Set
+
Reason
+
Expiry
+
Delete
+
+
+
+ <%~loop bans%> +
+
+ <%~set escaped_username = escape_url unescape_html $set_by_user_username%> + "><%nbsp set_by_user_username%> +
+
+ <%~set escaped_username = escape_url $user_username%> + <%if ban_type eq user_id_fk%>"><%nbsp user_username%><%endif%> +
+
+ <%if ban_type eq ban_ip%><%ban_ip%><%endif%> +
+
+ <%ban_set_date%> +
+
+ <%ban_reason%> +
+
+ <%~if ban_expiry%> + <%ban_expiry_date%> + <%~else%> + none + <%~endif%> +
+
+ +
+
+ <%~endloop%> +
+
+ +
+
+
+ +
+
+
+ <%~endif%> + +
<%-- end content --%> +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/forum/forum_preban.html b/site/forum/forum_preban.html new file mode 100644 index 0000000..032e25a --- /dev/null +++ b/site/forum/forum_preban.html @@ -0,0 +1,96 @@ + +<%~set search_widget = 1%> + + + <%cat_full_name%>: Ban +<%include include_common_head.html%> + + + +
+ <%include include_header.html%> + +
+
+ + + <%~set content_header_title = 'Ban '%> + <%~if in.ban_type eq ban_ip%><%set content_header_title .= 'IP Address'%><%else%><%set content_header_title .= 'User'%><%endif%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + <%~set breadcrumbs.1.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
<%-- start content--%> + <%include include_content_header.html%> + +

+ You have chosen to ban + <%~if ban_type eq ban_ip%> the IP address '<%ban_value%>' + <%~else%> the user <%include include_username.html%> + <%~endif%> + from <%cat_full_name%>: <%forum_name%>. +

+

+ If desired, you may set a timeout (in days) for the ban, and a + short reason for the ban, viewable only by administrators and + any other moderators of the forum. +

+ +
+ <%hidden_form%> + + + + + +
+
+
Ban Type
+
+
+ <%if ban_type eq ban_ip%>IP ban<%else%>User ban<%endif%> +
+
+
+
<%if ban_type eq ban_ip%>IP Address<%else%>User<%endif%>
+
+
+ <%if ban_type eq ban_ip%><%ban_value%><%else%><%include include_username.html%><%endif%> +
+
+
+
Ban Expiry
+
+
+ days +
+
+
+
Ban Reason
+
+
+ +
+
+ +
+
+
+
+
+
+
+ +
<%-- end content --%> +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/forum/forum_view.html b/site/forum/forum_view.html new file mode 100644 index 0000000..6e3e093 --- /dev/null +++ b/site/forum/forum_view.html @@ -0,0 +1,209 @@ + +<%~set search_widget = 1%> +<%~set poll_widget = 1%> + + + <%cat_full_name%>: <%forum_name%><%if this_page > 1%>: Page <%this_page%><%endif%> +<%include include_common_head.html%> + + +<%~set hidden_sort = ''%><%set hidden_page = ''%><%set form_sort = ''%><%set form_page = ''%> +<%~if in.sb%><%set hidden_sort .= ";sb=$sb"%><%set form_sort .= ""%><%endif%> +<%~if in.so%><%set hidden_sort .= ";so=$so"%><%set form_sort .= ""%><%endif%> +<%~if in.mh%><%set hidden_page .= ";mh=$mh"%><%set form_page .= ""%><%endif%> +<%~if in.page%><%set hidden_page .= ";page=$this_page"%><%set form_page .= ""%><%endif%> +<%~set do = ''%><%if in.do%><%set do = "do=$this_do;"%><%endif%> + +
+ <%include include_header.html%> + +
+
+ + <%~set content_header_title = $forum_name%> + <%~set btn_new = 1%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + + <%include include_breadcrumb.html%> + + <%~set paging_do = ''%> + <%~if in.do%> + <%~set paging_do = "do=$this_do"%> + <%~endif%> + <%~set pagination_options.type = 'forum'%> + <%~set pagination_options.id = $forum_id%> + <%~set pagination_options.params = "$paging_do$hidden_sort$hidden_page"%> + <%~set pagination_options.page_items = $forum_total_threads%> + +
<%-- start content--%> + <%include include_content_header.html%> +
+ <%include include_hot_topics.html%> + <%include include_quicklaunch.html%> +
+ +
<%-- start table --%> +
+
+
 
+
+ <%~if sb eq post_subject and so ieq asc%><%set url_param_subject_so = 'desc'%><%else%><%set url_param_subject_so = 'asc'%><%endif%> + " title="Sort by Subject"> + Subject + <%~if sb eq post_subject%> <%endif~%> + +
+
+ <%~if sb eq post_username and so ieq asc%><%set url_param_author_so = 'desc'%><%else%><%set url_param_author_so = 'asc'%><%endif%> + " title="Sort by Author"><%~%> + Author + <%~if sb eq post_username%> <%endif~%> + +
+
Views
+
+ <%~if sb eq post_replies and so ieq desc%><%set url_param_replies_so = 'asc'%><%else%><%set url_param_replies_so = 'desc'%><%endif%> + " title="Sort by # of Replies"><%~%> + Replies + <%~if sb eq post_replies%> <%endif~%> + +
+
+ <%~if sb eq post_latest_reply and so ieq asc%><%set url_param_last_post_so = 'desc'%><%else%><%set url_param_last_post_so = 'asc'%><%endif%> + " title="Sort by # of Replies" rel="nofollow"><%~%> + Last Post + <%~if sb eq post_latest_reply%> <%endif~%> + +
+
+
+
+ <%~loop post_loop%> + <%~if post_locked%><%set icon_class = 'locked-thread'%> + <%~elsif post_thread_hot%><%set icon_class = 'hot-thread'%> + <%~elsif post_new and new_replies%><%set icon_class = 'new-replies'%> + <%~elsif post_replies == 1%><%set icon_class = 'first-post'%> + <%~elsif post_replies%><%set icon_class = 'thread'%> + <%~elsif post_new%><%set icon_class = 'new-thread'%> + <%~else%><%set icon_class = 'single-thread'%><%endif%> +
+
+
+ <%~unless post_moved and post_moved_perm < 3%> + + <%~set forum_view = ''%> + <%~if in.do%> + <%~set forum_view = "forum_view=$this_do&"%> + <%~endif%> + <%~if post_moved%><%set url_id = $post_moved_id%><%else%><%set url_id = $post_id%><%endif%> + <%~set url_anchor = ""%> + <%~set url_param_unread = ""%> + <%~if current.user_default_post_display == 1 or not current.user_id%> + <%~if post_depth%> + <%~set url_anchor = "p$post_id"%> + <%~elsif current.user_jump_to_unread%> + <%~set url_param_unread = ";page=unread"%> + <%~set url_anchor = "unread"%> + <%~endif%> + <%~endif%> + <%~endunless%> + + <%~if post_sticky%> Sticky:<%endif%> + <%~if post_moved%> Moved:<%endif%> + + $url_id, params => "$forum_view$hidden_sort$url_param_unread", anchor => $url_anchor)%>" class="title"> + <% post_subject %> + +
+ by + <%~set escaped_username = escape_url $post_username%> + " class="postedby"><%post_username %> + <%if not post_moved%> + | <% post_date %>| <% post_replies || 0 %> replies + <%~if post_depth == 0%> + <%~set url_param_page_last = "post=last-"%> + <%~if post_moved%><%set url_param_page_last .= "$post_moved_id"%><%else%><%set url_param_page_last .= "$post_id"%><%endif%> + <%~set url_anchor = ""%> + <%~if post_display_is_flat%><%set url_anchor = "last"%><%endif%> + $url_anchor)%>" rel="nofollow" class="right-arrow">→ + <%~endif%> + <%~endif%> + + <%-- Server ad on mobile --%> + <%~if row_num == 4 %> + <%~set serve_ad = 1%><%set ad_count = 1%> + <%~else%> + <%~set count += 1%><%set odd = $count % 30%> + <%~if not $odd%> +
+ <%include include_poll_widget.html%> + <%~set serve_ad = 0%><%set count = 0%> +
+ <%endif%> + <%~endif%> + +
+ <%~if $serve_ad%><%set height_choice = $ad_count % 2%> + <%~if not height_choice%><%set ad_height = 250%><%else%><%set ad_height = 600 %><%endif%> + <%~set ad_count += 1%> + <%include include_ad_mobile.html%> + <%~set serve_ad = 0%><%set count = 0%> + <%~endif%> +
+
+ <%~set include_username_display = $post_username%> + <%include include_username.html%> +
+
<%if not post_moved%><%post_views%><%endif%>
+
<%if not post_moved%><%post_replies%><%endif%>
+
+ <%~if not post_moved%> + <%post_latest_reply_date%> + + <%~if post_replies%> + <%~set escaped_username = escape_url $post_latest_poster%> + by " class="postedby"><%post_latest_poster%> + <%~endif%> + + <%~-- Show jump image for root posts --%> + <%~if post_depth == 0%> + $url_anchor)%>" rel="nofollow" class="right-arrow">→ + <%~endif%> + + <%~endif%> +
+
+ <%~ endloop %> +
+
<%-- end dtable --%> + +
+
+ <%~set paging_do = ''%> + <%~if in.do%> + <%~set paging_do = "do=$this_do"%> + <%~endif%> + <%~set pagination_options.type = 'forum'%> + <%~set pagination_options.id = $forum_id%> + <%~set pagination_options.params = "$paging_do$hidden_sort$hidden_page"%> + <%~set pagination_options.page_items = $forum_total_threads%> + + <%include include_paging.html%> + <%include include_forum_actions.html%> + +
+ <%-- ad for tablet --%> + <%include include_ad_tablet.html%> + +
<%-- end content --%> +
<%-- end contentwrapper --%> +
<%-- end main --%> + <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/forum/include_ad.html b/site/forum/include_ad.html new file mode 100644 index 0000000..107b12d --- /dev/null +++ b/site/forum/include_ad.html @@ -0,0 +1,38 @@ +<%~if adsize eq '300x250' and $ad_300x250%> +<%~if admod eq 'mobile' or $admod eq 'tablet'%> +
+ <%~-- Serve ad for tablet/mobile --%> + +
+<%~else%> +
+ +
+<%~endif%> +<%~elsif adsize eq '300x600' and $ad_300x600%> +<%~if admod eq 'mobile' or $admod eq 'tablet'%> +
+ <%~-- Serve ad for tablet/mobile --%> + +
+<%~else%> +
+ +
+<%~endif%> +<%~elsif adsize eq '728x90' and $ad_728x90%> +
+ <%~-- Serve leaderboard ad --%> + +
+<%~endif%> diff --git a/site/forum/include_ad_mobile.html b/site/forum/include_ad_mobile.html new file mode 100644 index 0000000..9b5aaf8 --- /dev/null +++ b/site/forum/include_ad_mobile.html @@ -0,0 +1,8 @@ +<%~set admod = 'mobile'%> +<%~if ad_height == 250%> +<%~set adsize = '300x250'%> +<%~else%> +<%~set adsize = '300x600'%> +<%~endif%> +<%include include_ad.html%> +<%~set admod = ''%> diff --git a/site/forum/include_ad_tablet.html b/site/forum/include_ad_tablet.html new file mode 100644 index 0000000..feb399f --- /dev/null +++ b/site/forum/include_ad_tablet.html @@ -0,0 +1,4 @@ +<%~set admod = 'tablet'%> +<%~set adsize = '300x250'%> +<%include include_ad.html%> +<%~set admod = ''%> diff --git a/site/forum/include_attachments.html b/site/forum/include_attachments.html new file mode 100644 index 0000000..c52df34 --- /dev/null +++ b/site/forum/include_attachments.html @@ -0,0 +1,73 @@ +<%-- + The following loops through attachments that are either going to be added with the + post or message, or, when editing, the new ones to add AND the ones that already exist. + The following variables are available: + - att_id - the attachment ID + (in either post, message, or temp attachment directories) + - att_filename - the filename of the attachment + - att_size - the size of the attachment in bytes. Use + <%GForum::Attachment::friendly_size($att_size)%> to get a "friendly" size such as "66.3 KB" instead of "67890". + - att_content - the content type of the file (such as text/html for .html files). Use + <%GForum::Attachment::icon($att_content, $att_filename)%> to get the web path to the appropriate icon. + - att_type - usually 'temp', but will be 'post' for posts that already exist when editing a post +--%> +<%~if attach_mode eq message%> + <%~set show_inline_checkbox = $config.allow_message_inline%> +<%~else%> + <%~set attach_mode = 'post'%> + <%~set show_inline_checkbox = $forum_attachment_inline%> +<%~endif%> +<%~if this_do eq "${attach_mode}_attachment_list"%> +<%~set ADVANCEDEDITOR = 1%> +<%~endif%> +<%~if ADVANCEDEDITOR%> + +<%endif~%> +<%if attachments.length or can_attach%> +
+<%loop attachments%> +
+
+ + <%att_filename%> + <%GForum::Attachment::friendly_size($att_size)%> +
+ <%~if show_inline_checkbox%> +
+ <%if att_inline%><%endif%> + checked="checked"<%endif%> class="checkbox"<%if ADVANCEDEDITOR%> onclick="updateInline('<%escape_js att_filename%>', '<%att_type%>', '<%att_id%>', this.checked)"<%endif%> /> +
+ <%~endif%> + +
+<%endloop%> +<%if can_attach%> +
+
+ +
+ <%~if show_inline_checkbox%> +
+ +
+ <%~endif%> +
+ +
+
+<%endif%> +
+<%endif%> diff --git a/site/forum/include_bike.html b/site/forum/include_bike.html new file mode 100644 index 0000000..6b4744a --- /dev/null +++ b/site/forum/include_bike.html @@ -0,0 +1,35 @@ +
+
+ Name: <%bike_name%> + <%~if current_user_id == $user_id%> + [" rel="edit" title="Edit Settings">Edit] + <%~endif%> +
+ Style: <%bike_style%>
+ Brand: <%bike_brand%>
+ Model: "><%bike_model%>
+ Size: "><%bike_size%> +
+

A: Saddle height<%if bike_saddle_height%>
<%bike_saddle_height%><%endif%>

+

B: Plumb line, saddle nose to BB
<%if bike_plumb_line_pos == 0%>+<%elsif bike_plumb_line_pos == "1"%>-<%endif%><%if bike_plumb_line%><%bike_plumb_line%><%endif%>

+ <%~if bike_style eq 'Tri'%> +

C: Cockpit (nose to extension tip)
<%if bike_cockpit%><%bike_cockpit%><%endif%>

+

D: Armrests to extension tip
<%if bike_armrest_exttip%><%bike_armrest_exttip%><%endif%>

+

E: Armrests drop
<%if bike_armrestdrop_pos == 0%>-<%elsif bike_armrestdrop_pos == "1"%>+<%endif%><%if bike_armrestdrop%><%bike_armrestdrop%><%endif%>

+

F: Saddle nose to armrest back
<%if bike_nose_armrest%><%bike_nose_armrest%><%endif%>

+

G: Armrest width
<%if bike_armrest_width%><%bike_armrest_width%><%endif%>

+ <%endif%> + <%if bike_style eq 'Road'%> +

H: Saddle nose to stem clamp
<%if bike_nose_clamp%><%bike_nose_clamp%><%endif%>

+

I: Handlebar drop
<%if bike_handlebar_drop%><%bike_handlebar_drop%><%endif%>

+ <%endif%> + <%~if bikd_comments%> +

Comments: <%bike_comments%>

+ <%~endif%> +
+
+
+ View More +
+
+ diff --git a/site/forum/include_bike_form.html b/site/forum/include_bike_form.html new file mode 100644 index 0000000..1859e9b --- /dev/null +++ b/site/forum/include_bike_form.html @@ -0,0 +1,176 @@ + +Close +
+ + + + + + <%~if select_bike_id%> + + + <%else%> + + <%endif%> + <%hidden_form%> + +

<%if select_bike_id%>Edit<%else%>Add<%endif%> Bike

+

+
+
+
Bike Name
+
+
+ +
+
+
+
Style
+
+
+ <%Plugins::GForum::Bikes::get_field_def('bike_style')%> + +
+
+
+
Brand/Models
+
+
+ + +
+
+
+
Size
+
+
+ <%Plugins::GForum::Bikes::get_field_def('bike_size')%> + +
+
+
+
Saddle height
+
+
+ +
+
+
+
Plumb line, saddle nose to BB
+
+
+ <%Plugins::GForum::Bikes::get_field_def('bike_plumb_line_pos')%> + + +
+
+
+
Cockpit (nose to extension tip)
+
+
+ +
+
+
+
Armrests to extension tip
+
+
+ +
+
+
+
Armrests drop
+
+
+ <%Plugins::GForum::Bikes::get_field_def('bike_armrestdrop_pos')%> + + +
+
+
+
Saddle nose to armrest back
+
+
+ +
+
+
+
Armrest width
+
+
+ +
+
+
+
Saddle nose to stem clamp
+
+
+ +
+
+
+
Handlebar drop
+
+
+ +
+
+
+
Comments
+
+
+ +
+
+
+
+
+
+ <%~if select_bike_id%> + + + <%~else%> + + <%~endif%> +
+
+
<%--end dtable--%> +

All values in millimeters

+
+<%include include_bike_js.html%> diff --git a/site/forum/include_bike_js.html b/site/forum/include_bike_js.html new file mode 100644 index 0000000..a8321a6 --- /dev/null +++ b/site/forum/include_bike_js.html @@ -0,0 +1,66 @@ + + diff --git a/site/forum/include_bike_list.html b/site/forum/include_bike_list.html new file mode 100644 index 0000000..0fef895 --- /dev/null +++ b/site/forum/include_bike_list.html @@ -0,0 +1,11 @@ +
Bike(s) <%if current_user_id == $user_id%>you<%else%><%user_username%><%endif%> own
+<%if success%>

<%success%>

<%endif%> + +<%Plugins::GForum::Bikes::get_user_bikes($user_id)%> +<%~if user_bikes_loop.length%> +<%~loop user_bikes_loop%> +<%include include_bike.html%> +<%~endloop%> +<%~else%> +

No bike found.

+<%~endif%> diff --git a/site/forum/include_breadcrumb.html b/site/forum/include_breadcrumb.html new file mode 100644 index 0000000..0b679cf --- /dev/null +++ b/site/forum/include_breadcrumb.html @@ -0,0 +1,8 @@ + + diff --git a/site/forum/include_common_head.html b/site/forum/include_common_head.html new file mode 100644 index 0000000..8a9fa30 --- /dev/null +++ b/site/forum/include_common_head.html @@ -0,0 +1,5 @@ +<%~set minify = $config.minify || 0%> +<%~set minify = 0%> +<%~set slowtwitch_forum = 1%> +<%include include_global_head.html%> +<%google_analytics%> diff --git a/site/forum/include_content_header.html b/site/forum/include_content_header.html new file mode 100644 index 0000000..431f420 --- /dev/null +++ b/site/forum/include_content_header.html @@ -0,0 +1,43 @@ + + +<%~if errors.length%><%set error_loop = $errors%><%endif%> +<%~if success or success_loop.length%> +
+
    + <%~if success starts '
  • '%> + <%success%> + <%~elsif success%> +
  • <%success%>
  • + <%~endif%> + <%~loop success_loop%> +
  • <%loop_value%>
  • + <%~endloop%> +
+
+<%~endif%> +<%~if error or error_loop.length%> +
+
    + <%~if error starts '
  • '%> + <%error%> + <%~elsif error%> +
  • <%error%>
  • + <%~endif%> + <%~loop error_loop%> +
  • <%loop_value.error || $loop_value%>
  • <%-- error_loop values containing an 'error' key are used by GForum in some places, which were implemented before loop_value existed --%> + <%~endloop%> +
+
+<%~endif%> + +<%include include_forum_rules.html%> diff --git a/site/forum/include_forum_actions.html b/site/forum/include_forum_actions.html new file mode 100644 index 0000000..e8df77c --- /dev/null +++ b/site/forum/include_forum_actions.html @@ -0,0 +1,35 @@ +
+ <%~if page_id eq 'category_list' and not $current_user_id%> + You are not logged in. Click ">here to sign in. + + <%elsif page_id eq 'forum_view'%> + All Forums + <%~if btn_new and user_forum_permission >= 5%> + " class="btn default">post new topic + <%~endif%> + + <%~elsif page_id eq 'search_results'%> + All Forums + <%~elsif page_id eq 'user_view'%> + <%~if perm_hide_post%> + Show User's Posts + <%~else%> + Hide User's Posts + <%~endif%> + + <%~if user_id ne $current.user_id%> + <%~set my_username = $current.user_username%> + <%~if user_accept_privmsg == 2 and buddy.$my_username%><%set buddy_ok = 1%><%endif~%> + <%~if user_accept_privmsg == 1 or buddy_ok~%> + " class="btn default">Send Private Message + <%~endif%> + <%~endif%> + + <%~elsif page_id like 'message'%> + <%~if not url_param_mh%><%set url_param_mh = ''%><%endif%> + " title="Private Messages - Inbox" class="btn default">Inbox + " title="Private Messages - Sent" class="btn default">Sent + " title="Private Messages - Compose" class="btn default">Compose + + <%~endif%> +
diff --git a/site/forum/include_forum_rules.html b/site/forum/include_forum_rules.html new file mode 100644 index 0000000..64ccf07 --- /dev/null +++ b/site/forum/include_forum_rules.html @@ -0,0 +1,4 @@ +
+

Login required to started new threads

+

Login required to post replies

+
diff --git a/site/forum/include_hot_topics.html b/site/forum/include_hot_topics.html new file mode 100644 index 0000000..c467352 --- /dev/null +++ b/site/forum/include_hot_topics.html @@ -0,0 +1,29 @@ +<%~if not tagged_threads.length%><%~set tagged_threads = GForum::ThreadTag::generate_threadtags()%><%endif%> +<%~if tagged_threads.length%> +
+ Hot Forum Topics +
+ +
+
+<%~endif%> diff --git a/site/forum/include_markup_tags.html b/site/forum/include_markup_tags.html new file mode 100644 index 0000000..e966302 --- /dev/null +++ b/site/forum/include_markup_tags.html @@ -0,0 +1,27 @@ +
+<%-- + + --%> + + + + + + + +
+ diff --git a/site/forum/include_message_display.html b/site/forum/include_message_display.html new file mode 100644 index 0000000..321ae34 --- /dev/null +++ b/site/forum/include_message_display.html @@ -0,0 +1,83 @@ +<%~if preview%> +
Preview:
+<%~elsif reply_view%> +
Replying to:
+<%~endif%> + +
+
+ + <%msg_subject%> + +
+ <%unless preview or reply_view%> + <%~unless sent_message%> + " title="Reply">Reply + <%~if config.message_style % 2%> + | " title="Quote">Quote + <%~endif%> + <%endunless%> + | Delete + <%endunless%> +
+
+
+
+
+
+
+
+ <%include include_message_display_infobar.html%> +
+
+
+ <%msg_body%> +
+ + <%~if sent_message and msg_has_attachments%> + <%~set show_attachments = 1%> + <%~else%> + <%~loop msg_attachments%> + <%~if messatt_inline and config.allow_message_inline%><%nextloop%><%endif%> + <%~set show_attachments = 1%> + <%~lastloop%> + <%~endloop%> + <%~endif%> + + <%~if show_attachments%> +
+ +
+ <%~endif%> + +
+
<%-- drow --%> +
<%-- dtable --%> +
+ <%unless preview or reply_view%> + <%~unless sent_message%> + " title="Reply" class="btn">Reply + <%~if config.message_style % 2%> + " title="Quote" class="btn">Quote + <%~endif%> + <%endunless%> + Delete + <%endunless%> +
+
+
diff --git a/site/forum/include_message_display_infobar.html b/site/forum/include_message_display_infobar.html new file mode 100644 index 0000000..84480c7 --- /dev/null +++ b/site/forum/include_message_display_infobar.html @@ -0,0 +1,11 @@ +<%~if preview%><%current%><%endif%> + diff --git a/site/forum/include_message_list.html b/site/forum/include_message_list.html new file mode 100644 index 0000000..193136a --- /dev/null +++ b/site/forum/include_message_list.html @@ -0,0 +1,30 @@ +<%~if messages.length%> +
+ <%~loop messages%> +
+
+ +
+
+ <%~set title = ''%><%set icon_class = ''%> + <%~if msg_status == 0%> + <%~set title = 'New message!'%><%set icon_class = 'sprite-message'%> + <%~elsif msg_status == 2%> + <%~set title = 'Replied message'%><%set icon_class = 'sprite-message-replied'%> + <%~else%> + <%~set title = 'Read message'%><%set icon_class = 'sprite-message-read'%> + <%~endif%> + +
+ +
+ <%set include_username_display = $msg_username~%> + <%include include_username.html%> +
+
<%msg_date%>
+
+ <%~endloop%> +
+<%endif%> diff --git a/site/forum/include_message_list_sent.html b/site/forum/include_message_list_sent.html new file mode 100644 index 0000000..d3b9bb8 --- /dev/null +++ b/site/forum/include_message_list_sent.html @@ -0,0 +1,24 @@ +<%if sent_messages.length%> +
+ <%loop sent_messages%> +
+
+ +
+
+ +
+ +
+ <%set include_username_display = $msg_username~%> + <%include include_username.html%> +
+
+ <%msg_date%> +
+
+ <%endloop%> +
+<%endif%> diff --git a/site/forum/include_message_write.html b/site/forum/include_message_write.html new file mode 100644 index 0000000..010eb66 --- /dev/null +++ b/site/forum/include_message_write.html @@ -0,0 +1,179 @@ +<%~if ie_version >= 5.5 or mozilla_version >= 1.4 or webkit_version >= 312.1%> + <%~if advanced_editor%> + <%~set ADVANCEDEDITOR = 1%> + <%~elsif current.user_advanced_editor and not basic_editor%> + <%~set ADVANCEDEDITOR = 1%> + <%~else%> + <%~set ADVANCEDEDITOR = 0%> + <%~endif%> +<%~else%> + <%~set ADVANCEDEDITOR = 0%> +<%~endif%> +<%~if ADVANCEDEDITOR and not config.message_style % 2%> + <%~set ADVANCEDEDITOR = 0%> +<%~endif%> + + + + + + +
+
+ +
+ +
+
+ +
+ for="autocomplete"<%endif%> class="name">Recipient +
+ <%~if ask_username%> + + ">View Member Directory + <%~else%> + + +

<%include include_username.html%>

+ <%~endif%> +
+
+
+ +
+ +
+ + +<%if ADVANCEDEDITOR%> + + + + + + + + +<%else%> + + + +<%endif%> +
+
+ +<%if not ADVANCEDEDITOR%> +<%include include_markup_tags.html%> +<%endif%> + +
+ <%if ADVANCEDEDITOR%><%-- The advanced editor only works for markup posts --%> + + + Switch to Basic Editor | + + <%~elsif ie_version >= 5.5 or mozilla_version >= 1.4 or webkit_version >= 312.1%> + Switch to Advanced Editor | + <%endif%> + Check Spelling | + Preview Post +
+ +<%~if can_attach or attachments.length%> +
+ +
+ <%~set attach_mode = 'message'%> + <%include include_attachments.html%> +
+
+<%~endif%> + +
+ +
+
+ checked="checked"<%endif%> class="checkbox" tabindex="8" /> + +
+
diff --git a/site/forum/include_paging.html b/site/forum/include_paging.html new file mode 100644 index 0000000..f8f651c --- /dev/null +++ b/site/forum/include_paging.html @@ -0,0 +1,10 @@ +<%~set pagination_options.style ||= 1%> +<%~set pagination_options.style_next = 'Next ' %> +<%~set pagination_options.style_prev = ' Prev' %> +<%~set pagination_options.style_first = '' %> +<%~set pagination_options.style_last = '' %> +<%~set pagination_options.style_nofirst = '' %> +<%~set pagination_options.style_nolast = '' %> +<%~set pagination_options.style_nonext = 'Next ' %> +<%~set pagination_options.style_noprev = ' Prev' %> +
<%~GForum::GUI::draw(draw => 'luna-paging')%>
diff --git a/site/forum/include_poll.html b/site/forum/include_poll.html new file mode 100644 index 0000000..302bf41 --- /dev/null +++ b/site/forum/include_poll.html @@ -0,0 +1,26 @@ +
+<%if remote%> + +<%endif%> + + + + +
<%poll_question%>
+
<%poll_text%>
+
+ <%loop poll_answers%> +
+ +
+ <%endloop%> +
+
+ + <%~if in.call_from eq 'glinks'%> + View Results + <%~else%> + " title="Poll view" class="lnk">View Results + <%~endif%> +
+
diff --git a/site/forum/include_poll_result.html b/site/forum/include_poll_result.html new file mode 100644 index 0000000..d8e5f64 --- /dev/null +++ b/site/forum/include_poll_result.html @@ -0,0 +1,26 @@ +
<%poll_question%>
+
<%poll_text%>
+
+ <%~if poll_type%> + Correct Answers: <%poll_correct_answer%> (<%poll_votes%> answers submitted) + <%~else%> + <%poll_votes%> Votes + <%~endif%> +
+
+ <%loop poll_answers%> + <%~set meter = $poll_answer_percentage * 80%><%set meter = $meter / 100%> +
+
<%poll_answer_percentage || 0%>%
+
+
<%poll_answer_answer%>
+
+ <%endloop%> +
+
+ <%~if in.call_from eq 'glinks'%> + View Poll + <%~else%> + " title="Poll view" class="btn">View Poll + <%~endif%> +
diff --git a/site/forum/include_poll_widget.html b/site/forum/include_poll_widget.html new file mode 100644 index 0000000..01806ab --- /dev/null +++ b/site/forum/include_poll_widget.html @@ -0,0 +1,28 @@ +<%~get_cgi_value('poll_view')%> +<%~if cgi_poll_view eq 'vote_view'%> +<%~Poll::list($current_poll, 'vote_view')%> +<%~elsif cgi_poll_view eq 'results_view'%> +<%~Poll::list($current_poll, 'results_view')%> +<%~else%> +<%~Poll::list($current_poll)%> +<%~endif%> + +<%~if poll_loop.length%> +
+

Poll

+
+<%loop poll_loop%> + <%if remote eq 'view'%> + <%include include_poll_result.html%> + <%elsif no_results%> + <%include include_poll.html%> + <%else%> + <%include include_poll_result.html%> + <%endif%> +<%endloop%> +
+
+<%~if slowtwitch_forum%> +
+<%~endif%> +<%~endif%> diff --git a/site/forum/include_post_display.html b/site/forum/include_post_display.html new file mode 100644 index 0000000..aee0337 --- /dev/null +++ b/site/forum/include_post_display.html @@ -0,0 +1,182 @@ +<%~if hide_post%> +
+
+ Ignored Post +
+ Show This Post +
+
+
+
+
+
+ +
+
+
+
+
+
+
+<%endif%><%-- if hide_post --%> + +<%~if preview%> +
Preview:
+<%~elsif reply_view%> +
Replying to:
+<%~elsif orig_view%> +
Original post:
+<%~endif%> + +
+
+ <%if last%><%endif%><%if first_new%><%endif%> + + <%~if post_deleted%> + Post deleted by <%post_deleted_by%><%if user_perm_moderator%>: <%post_subject%><%endif%> + <%~else%> + <%~if post_moved%>Moved: <%endif%> + <%if post_subject%><%~post_subject%><%else%> <%endif%> + <%~if post_new%> - NEW<%endif%> + <%~endif~%> + + <%if not preview and not hide_post_menu and post_father_id%> + <%~ifnot post_father_on_page%> + <%~set do = ''%> + <%~if in.do%> + <%~set do = "do=$this_do"%> + <%~endif%> + [ $post_father_id, params => "$do$hidden_sort", anchor => "p$post_father_id")%>" rel="nofollow">In reply to ] + <%~else%> + [ In reply to ] + <%~endif%> + <%endif%> + + +
+ <%GForum::GUI::draw( + draw => post_display_options, + edit => "Edit", + delete => "Delete", + cant_post => "Can't Post", + quote => "Quote", + reply => "Reply", + order => "edit,delete,quote,reply", + a_attribs => "", + separator => " | ", + )%> +
+
+ +
+
+
+
+ <%include include_post_display_infobar.html%> +
+
+
+ <%~if not post_deleted or user_perm_moderator%> + <%Slowtwitch::Post::parse_youtube($post_message)%> + <%post_message%> + <%~endif%> +
+ <%~set show_post_notes = 0%><%-- Determine whether or not we need the "post-notes" div --%> + <%~if this_do eq post_preview and in.redo eq post_edit and preview%><%set show_post_notes = 1%> + <%~elsif post_deleted and user_perm_moderator%><%set show_post_notes = 1%> + <%~elsif post_keep and user_perm_moderator%><%set show_post_notes = 1%> + <%~elsif post_moved or post_last_edit_username%><%set show_post_notes = 1%> + <%~endif%> + + <%~if show_post_notes%> +
+ <%~if this_do eq post_preview and in.redo eq post_edit and preview%> +
+ <%~set escaped_username = escape_url unescape_html $current.user_username%> + Last edited by: "><%current.user_username%>: <%GForum::date($TIME)%> +
+ <%~elsif post_last_edit_username%> +
+ <%~set escaped_username = escape_url $post_last_edit_username%> + Last " rel="nofollow">edited by: + "><%post_last_edit_username%>: <%post_last_edit_date%> +
+ <%~endif%> + <%~if post_deleted and user_perm_moderator%> +
+ Deleted by: + <%~set escaped_username = escape_url $post_deleted_by%> + "><%post_deleted_by%><%if post_deleted_time%>: <%post_deleted_date%><%endif%> +
+ <%~endif%> + <%~if post_moved%><%-- An empty pointer to the moved post --%> +
+

Moved:

+ $post_moved_id)%>">Original post +
+ <%~endif%> +
+ <%~endif%> + + <%~set post_attachments_count = 0%> + <%~loop post_attachments%> + <%~if postatt_inline%><%if forum_attachment_inline or not forum_id%><%nextloop%><%endif%><%endif%> + <%~set post_attachments_count += 1%> + <%~endloop%> + <%~if tags.length or post_attachments_count%> +
+ <%~-- /div class="post-footer" --%> +
<%~-- /div class="top-spacer" --%> + <%~endif%> + + <%~if $serve_ad%> + <%include include_ad_mobile.html%> + <%~set serve_ad = 0%><%set count = 0%> + <%~endif%> +
+
<%-- drow --%> +
<%-- dtable --%> + +
+ <%GForum::GUI::draw( + draw => post_display_options, + edit => "Edit", + delete => "Delete", + cant_post => "Can't Post", + quote => "Quote", + reply => "Reply", + order => "edit,delete,quote,reply", + a_attribs => "class='btn'", + separator => " ", + )%> +
+
+ +
diff --git a/site/forum/include_post_display_infobar.html b/site/forum/include_post_display_infobar.html new file mode 100644 index 0000000..2cb1224 --- /dev/null +++ b/site/forum/include_post_display_infobar.html @@ -0,0 +1,93 @@ + diff --git a/site/forum/include_post_list.html b/site/forum/include_post_list.html new file mode 100644 index 0000000..e327c92 --- /dev/null +++ b/site/forum/include_post_list.html @@ -0,0 +1,65 @@ +
+
+
+
+
Subject
+
Username
+
Forum
+
Replies
+
Post time
+
+
+ +<%~if post_list.length%> +
+<%~loop post_list%> +
+
+ <%~if post_list_show_checkbox%> + + <%else%> + <%~if post_locked%><%set icon_class = 'locked-thread'%> + <%~elsif post_thread_hot%><%set icon_class = 'hot-thread'%> + <%~elsif post_new and new_replies%><%set icon_class = 'new-thread'%> + <%~elsif post_replies == 1%><%set icon_class = 'first-post'%> + <%~elsif post_replies%><%set icon_class = 'thread'%> + <%~else%><%set icon_class = 'single-thread'%><%endif%> + + <%~endif%> +
+
+ <%~if post_sticky%> Sticky:<%endif%> + <%~if post_moved%> Moved:<%endif%> + <%~set url_param_post_ist_query_string = ""%> + <%~if post_list_query_string%><%set url_param_post_ist_query_string = "$post_list_query_string"%><%endif%> + $post_id, params => $url_param_post_ist_query_string, anchor => "p$post_id")%>"> + <%~if post_deleted%> + Post deleted by <%post_deleted_by%><%if user_perm_moderator%>: <%post_subject%><%endif%> + <%~else%> + <%~post_subject%> + <%~endif%> + <%~if post_moved%> + Moved: + <%~endif%> + + <%~set cat_id ||= $cat_id_fk%> +
+ by " class="postedby"><%post_username%> | <% post_date %>| <% post_replies || 0 %> replies
+ <%GForum::Category::full_name_linked($cat_id, ': ')%>: $forum_id)%>"><%forum_name%> +
+
+
+ <%~set user_id ||= $user_id_fk%> + <%~set include_username_display = $post_username%> + <%include include_username.html%> +
+
+ <%GForum::Category::full_name_linked($cat_id, ': ')%>: $forum_id)%>"><%forum_name%> +
+
<%post_replies%>
+
<%post_date%>
+
+<%~endloop%> +
+<%~endif%> +
diff --git a/site/forum/include_post_toolbar.html b/site/forum/include_post_toolbar.html new file mode 100644 index 0000000..c1d86d7 --- /dev/null +++ b/site/forum/include_post_toolbar.html @@ -0,0 +1,27 @@ +<%~set toolbar_id += 1~%> +<%if toolbar_id == 1%> + +<%else%> +
+ <%hidden_form%> +
+ +<%~endif%> diff --git a/site/forum/include_post_write.html b/site/forum/include_post_write.html new file mode 100644 index 0000000..3b8cf4e --- /dev/null +++ b/site/forum/include_post_write.html @@ -0,0 +1,266 @@ +<%~if ie_version >= 5.5 or mozilla_version >= 1.4 or webkit_version >= 312.1%> + <%~if advanced_editor%> + <%~set ADVANCEDEDITOR = 1%> + <%~elsif current.user_advanced_editor and not basic_editor%> + <%~set ADVANCEDEDITOR = 1%> + <%~else%> + <%~set ADVANCEDEDITOR = 0%> + <%~endif%> +<%~else%> + <%~set ADVANCEDEDITOR = 0%> +<%~endif%> +<%~if ADVANCEDEDITOR and not forum_style % 2%> + <%~set ADVANCEDEDITOR = 0%> +<%~endif%> + + + + + +
+
+ +
+
+ + <%~if this_do eq post_edit%> +
+ +
+
+ <%~endif%> + + <%~if current.user_id and config.post_user_custom_username and this_do ne post_edit%> +
+ +
+
+ <%elsif not current.user_id%> + <%~if anonymous.length > 1 or config.post_guest_custom_username%> +
+ +
+ <%~if anonymous.length > 1%> + + <%~else%> + + <%~endif%> + <%~if config.post_guest_custom_username%> + + <%~endif%> +
+
+ <%~endif%><%-- endif anonymous.length > 1 ... --%> + +
+ +
+
+ <%endif%> + +
+ +
+ <%if ADVANCEDEDITOR%><%-- The advanced editor only works for markup posts --%> + + +

<%GForum::language('FORUM_STYLE_MARKUP')%>

+ <%elsif forum_style%><%-- forum_style is set, which means the user gets a choice --%> + + <%~if forum_style % 2%> + + — + Get Markup Help + + <%~if not forum_style_selected % 2%> + + <%~endif%> + <%~endif%> + <%~else%><%-- plain text only --%> + + <%GForum::language('FORUM_STYLE_PLAIN')%> + <%~endif%> +
+
+
+ +
+ +
+ +<%if ADVANCEDEDITOR%> + + + + + + + + +<%else%> + + +<%endif%> +
+
+ +<%if not ADVANCEDEDITOR%> +<%include include_markup_tags.html%> +<%endif%> + +
+ <%if ADVANCEDEDITOR%><%-- The advanced editor only works for markup posts --%> + + + Switch to Basic Editor | + + <%~elsif forum_style % 2%> + Switch to Advanced Editor | + <%endif%> + Check Spelling | + Preview Post +
+ +<%~if current.user_status == 3%> + +<%~endif%> + +<%if can_attach or attachments.length%> +
+ +
+ <%include include_attachments.html%> +
+
+<%endif%> + +
+ +<%if current.user_id%> +
+
+ checked="checked"<%endif%> class="checkbox" tabindex="8" /> + +
+
+ checked="checked"<%endif%> class="checkbox" tabindex="8" /> + +
+ <%~if current.user_forum_permission >= $FORUM_PERM_MODERATOR%> + <%~if root.post_sticky%> +
+ + +
+ <%~else%> +
+ checked="checked"<%endif%> class="checkbox" tabindex="8" /> + +
+ <%~endif%> + <%~if root.post_locked%> +
+ + +
+ <%~else%> +
+ checked="checked"<%endif%> class="checkbox" tabindex="8" /> + +
+ <%~endif%> + <%~endif%> +
+<%endif%> + +
+ <%include markup_help.html%> +
diff --git a/site/forum/include_quicklaunch.html b/site/forum/include_quicklaunch.html new file mode 100644 index 0000000..6d90ab9 --- /dev/null +++ b/site/forum/include_quicklaunch.html @@ -0,0 +1,10 @@ + diff --git a/site/forum/include_sidebar.html b/site/forum/include_sidebar.html new file mode 100644 index 0000000..54ec348 --- /dev/null +++ b/site/forum/include_sidebar.html @@ -0,0 +1,20 @@ +<%~if page_id eq 'category_list'%> + +<%~elsif search_widget or $forum_id%> +<%include include_sidebar_search.html%> +<%~endif%> + +<%~if page_id ne 'category_list' and $poll_widget%> +<%~set adsize = '300x250'%> +<%include include_ad.html%> + +<%~if poll_widget or $forum_id%> +<%include include_poll_widget.html%> +<%~endif%> +<%~endif%> + +<%~set adsize = '300x600'%> +<%include include_ad.html%> + diff --git a/site/forum/include_sidebar_search.html b/site/forum/include_sidebar_search.html new file mode 100644 index 0000000..03bd6e1 --- /dev/null +++ b/site/forum/include_sidebar_search.html @@ -0,0 +1,26 @@ + +<%include include_hot_topics.html%> diff --git a/site/forum/include_smilies_write.html b/site/forum/include_smilies_write.html new file mode 100644 index 0000000..b26a00c --- /dev/null +++ b/site/forum/include_smilies_write.html @@ -0,0 +1,6 @@ +<%smilies%> +
    +<%~loop smilies_loop%> +
  • <%alt%>
  • +<%~endloop%> +
diff --git a/site/forum/include_user_favourites.html b/site/forum/include_user_favourites.html new file mode 100644 index 0000000..7e77755 --- /dev/null +++ b/site/forum/include_user_favourites.html @@ -0,0 +1,35 @@ +<%~set pagination_options.params = "do=$this_do;sb=$sb;mh=$mh;user_id=$user_id"%> +<%~set pagination_options.page_items = $num_posts%> + +
+ + + + <%~set post_list = $favourite_posts%> + <%~set post_list_show_replies = 1%> + <%~set post_list_show_faves = 1%> + <%~set post_list_empty_message = 'You do not have any favourite posts'%> + <%~if user_username eq $current_user_username%> + <%~set post_list_show_checkbox = 1%> + <%~endif%> + <%~include include_post_list.html%> + + <%~if $num_favourites%> +
+
+ <%~set paging_do = ''%> + <%~if in.do%> + <%~set paging_do = "do=$this_do"%> + <%~endif%> + <%~set pagination_options.page_items = $num_favourites%> + + <%include include_paging.html%> + <%if current_user_username eq $user_username%> +
+ +
+ <%~endif%> +
+ <%~endif%> +
+ diff --git a/site/forum/include_username.html b/site/forum/include_username.html new file mode 100644 index 0000000..49e60bd --- /dev/null +++ b/site/forum/include_username.html @@ -0,0 +1,55 @@ +<%~-- + +This page is included almost everywhere in the templates to display a username. +Things like linking, group highlighting, buddy highlighting, etc. are handled +here, in one central place, instead of being duplicated all over the place. +The general usage is, instead of something like: + +... <%user_username%> ... + +you do: + +... <%include include_username.html%> ... + +This will take care of special classes for buddies, administrators, disabled +users, groups, moderators, etc., as well as + +In many cases it is necessary to display something other than user_username -- +for example, for posts and messages -- in which case you can set the +include_username_display variable to the username to display (it will be +cleared after being used). This also handles the case where +include_username_display is set, but user_id isn't set, as an indication of a +deleted user, in which case you get a ... + +Also note that this include will produce *no* whitespace around the or + tag returned, so whatever whitespace padding you put or don't put around +the include will be the only whitespace present. + +Some notable exceptions to the ability to use this include exist where only a +username is stored -- the last poster data for forums and threads is a good +example of this. + +--~%> +<%~if not user_id and include_username_display~%> + <%include_username_display%><%set include_username_display = ''%> +<%~else%> + + <%~set escaped_username = escape_url unescape_html $user_username%> + " title=" + <%~if current.user_status == $ADMINISTRATOR and not user_enabled%>User Disabled — <%endif%> + <%~if user_status == $ADMINISTRATOR%>Administrator — <%elsif forum_id and moderator.$user_id%>Moderator — <%endif%> + <%~if not user_status%>Guest — <%elsif current.user_id and current.buddy.$user_id%>Buddy — <%endif~%> + View <%user_username%>'s profile" class="username + <%~loop groups%> gr-<%loop_value.group_id%><%endloop%> + <%~if current.user_id and current.user_id == $user_id%> self<%endif%> + <%~if current.user_id and current.buddy.$user_username%> buddy<%endif%> + <%~if forum_id and moderator.$user_id%> moderator<%endif%> + <%~if user_status == $ADMINISTRATOR%> administrator<%endif%> + <%~if user_status == $ANONYMOUS%> anonymous<%endif%> + <%~if current.user_status == $ADMINISTRATOR and not user_enabled%> disabled<%endif~%> + "> + <%~if include_username_display%><%include_username_display%><%set include_username_display = ''%> + <%~else%><%user_username%> + <%~endif~%> + +<%~endif~%> diff --git a/site/forum/ip_posts.html b/site/forum/ip_posts.html new file mode 100644 index 0000000..0756d4b --- /dev/null +++ b/site/forum/ip_posts.html @@ -0,0 +1,115 @@ + + + + <%cat_full_name%>: IP details: <%ip%>: Posts +<%include include_common_head.html%> + + + +
+ <%include include_header.html%> + +
+
+ + + <%set content_header_title = "Posts From "%> + <%set content_header_title .= $ip%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
<%-- start content--%> + <%include include_content_header.html%> + + <%~if not ip_posts.length%> +

+ No posts have been made + <%if search_user%>by <%include include_username.html%><%endif%> + from the <%ip%><%if ip ends '.*'%> IP addresses<%endif%>. +

+ <%~else%> +

+ The following posts were made + <%if search_user%>by <%include include_username.html%><%endif%> + from the <%ip%><%if ip ends '.*'%> IP addresses<%endif%>. +

+ + <%~unless current.user_status = $ADMINISTRATOR%> +

+ Note that information is only shown for forums which you moderate. +

+ <%~endunless%> + +

+ " title="IP summary">Click here to view an IP summary. +

+ +
+
+
+ <%~if show_ip_column%> +
IP Address
+ <%~endif%> +
Poster
+
Post
+
Forum
+
Date
+
+
+ +
+ <%~loop ip_posts%> +
+ <%~if show_ip_column%> + + <%~endif%> +
+ <%include include_username.html%> + <%~if user_id%> + (<%~%> + <%~set url_param_username = escape_url unescape_html $user_username%> + " title="View user's IP addresses">IPs + <%~if current.user_status == $ADMINISTRATOR and user_status != $ADMINISTRATOR%> + | + <%~if user_enabled%><%set url_param_user_enabled = 'dis'%><%else%><%set url_param_user_enabled = 'en'%><%endif%> + "<%if not user_enabled%> title="User account is disabled"<%endif%>><%if user_enabled%>Dis<%else%>En<%endif%>able User + <%~endif~%> + ) + <%~endif%> +
+
+ <%if post_moved%>Moved: <%endif~%> + $post_id, anchor => "p$post_id")%>"><%post_subject%> +
+ by " class="postedby"><%post_username%> | <%post_date%>
+ <%GForum::Category::full_name_linked($cat_id, ': ')%>: $forum_id)%>"><%forum_name%> +
+
+
+ <%GForum::Category::full_name_linked($cat_id, ': ')%>: + $forum_id)%>"><%forum_name%> +
+
+ <%post_date%> +
+
+ <%~endloop%> +
+
+ <%~endif%> + +
<%-- end content --%> +
+
+ <%include include_footer.html%> +
+ + +<%~include include_global_js.html%> + diff --git a/site/forum/ip_summary.html b/site/forum/ip_summary.html new file mode 100644 index 0000000..ecc57ad --- /dev/null +++ b/site/forum/ip_summary.html @@ -0,0 +1,97 @@ + + + + <%cat_full_name%>: IP summary: <%ip%> +<%include include_common_head.html%> + + + +<%~set content_header_title = 'IP Summary'%> + + +
+ <%include include_header.html%> + +
+
+ + + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
<%-- start content--%> + <%include include_content_header.html%> + <%~if not ips.length%> +

No one has posted to <%cat_full_name%> using the IP addresses <%ip%>.

+ <%~else%> +

The following <%ip%> IP addresses have posted to <%cat_full_name%>.

+ + <%~unless current.user_status == $ADMINISTRATOR%> +

Note that information is only shown for forums which you moderate.

+ <%~endunless%> + +

" title="Extended post details">Click here to view extended post details.

+ +
+
+
+
IP Address
+
Posts
+
Posters
+
Latest Post
+
+
+
+ <%~loop ips%> +
+
+ <%ip%> + <%~if country_lookups%> + (<%if ip_country%><%ip_country%><%else%>Unknown<%endif%>) + <%~endif%> +
+ +
+ <%loop ip_posters~%> + + <%include include_username.html%> + (<%~%> + <%~set url_username = escape_url unescape_html $user_username%> + "><%post_count%> posts + | + " title="View user's IP addresses">IPs + <%~if current.user_status == $ADMINISTRATOR and user_status != $ADMINISTRATOR%> + | + <%~if user_enabled%><%set url_param_user_enabled ="dis"%><%else%><%set url_param_user_enabled ="en"%><%endif%> + "<%if not user_enabled%> title="User account is disabled"<%endif%> rel="post-only"><%if user_enabled%>Dis<%else%>En<%endif%>able User + <%~endif~%> + )<%~%> + + <%~unless last%>, <%endunless%> + <%~endloop%> +
+
<%GForum::date($latest_post)%>
+
+ <%~endloop%> +
+
+ <%~endif%> + +
<%-- end content --%> +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/forum/local/category_list.html b/site/forum/local/category_list.html new file mode 100644 index 0000000..7571978 --- /dev/null +++ b/site/forum/local/category_list.html @@ -0,0 +1,175 @@ + + + + <:: Slowtwitch Forums ::> + <%include include_common_head.html%> + + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> + +
+ <%include include_header.html%> + +
+
+ + + <%~set content_header_title = 'Forum'%> + <%include include_breadcrumb.html%> + +
<%-- start content--%> + <%~loop everything%> + <%~if not forum_id and $current.user_id and $cat_show_forums and $main_page%> + <%~set url_param_hide_cat = "hide_cat=$cat_id"%> + <%~if cat_hidden%><%set url_param_hide_cat = "un$url_param_hide_cat"%><%endif%> + " title="<%if cat_hidden%>Expand<%else%>Collapse<%endif%> this category" rel="nofollow"><%if cat_hidden%>Expand<%else%>Collapse<%endif%> <%cat_name%> + <%~endif%> + <%~endloop%> + + <%include include_content_header.html%> + +
<%-- start table --%> +
+
+
 
+
Forum Name
+
Threads
+
Posts
+
Last Post
+
+
+
+ <%~loop everything%> + <%~if forum_id and not forum_archived%> +
+
+
+ $forum_id) %>" class="title"><% forum_name %> +
+ <%~set escaped_username = escape_url $forum_last_poster%> + by " title="<% forum_last_poster %>" class="postedby"><% forum_last_poster %> | <%forum_last_date%> + "last")%>" rel="nofollow" class="right-arrow">→ +
+ <%~if forum_desc%> +

<%forum_desc%>

+ <%~endif%> + <%~if forum_id == 7 %> + <%-- Serve ad on mobile --%> + <%include include_ad_mobile.html%> + <%~endif%> +
+
<%forum_total_threads || 0%>
+
<%forum_total || 0%>
+ +
+ <%~endif%> + <%~endloop%> +
+
<%-- end dtable --%> + +
+ + + +
<%-- end content --%> +
+
+ <%include include_footer.html%> +
+
+
+
+
+
Hot thread
+
+
+
+
Post with replies
+
+
+
+
Single post
+
+
+
+
The thread is locked
+
+
+
+
First post in thread
+
+
+
+
Jump to the last post in thread
+
+
+
+
Unread message
+
+
+
+
Read message
+
+
+
+
Replied message
+
+
+
+ +
+ +
+

IS THIS A PUBLIC FORUM?

+

No, this is a private forum open to the public in general, but with certain restrictions including but not limited to those listed herein. Using this forum is a privilege, not a protected right, and this privilege can be revoked at our sole discretion.

+

IS THIS A MODERATED FORUM?

+

Yes, but moderation occurs after the fact, that is, we don't monitor a post before it is published, you publish it and then we eventually read it (or not). This forum receives between 1500 and 2500 posts every day, on average, too many posts to read and monitor. Accordingly, anything you see that you consider abusive or in bad taste; if you read a post that you think ought to be brought to our attention; please apprise us of this. In such case, the editorial email prompt is at the bottom of every page of the Slowtwitch.com editorial website (www.slowtwitch.com). That email address to use for such concerns is: slowman@slowtwitch.com

+

EXPECTATION OF ANONYMITY

+

You have none. There is none. Slowtwitch.com does not warrant that your anonymity will be protected, nor that we will protect it. If the preservation of your anonymity is vital to you, don't post on any Slowtwitch forum.

+

WHAT CAN I POST HERE?

+

Anything associated with swim, bike, run, or triathlon in general. Off-topic posts belong in the Lavender Room. Notices belong in Notices, jobs sought or advertised in Jobs, items for sale in Classifieds. There are rules for each of these forums posted in "sticky" threads on the tops of each of those forums.

+

WHAT CAN'T I POST HERE?

+

We are especially cranky about trial-by-internet. Do not register to post on this forum only to have your first post be a complaint against a company doing business in the sport of triathlon. Do not lodge a complaint against a named entity unless you yourself are willing to be identifiable, that is, no anonymous rants. Do not EVER say somebody's product is unsafe unless you have rock-solid demonstrable proof. This is as much for your benefit as anyone's (we'd prefer your hot temper doesn't result in a lawsuit a manufacturer lodges against you). Do not accuse anyone of taking performance enhancing drugs.

+

WHAT IS CONSIDERED ABUSIVE?

+

We don't know, but we know it when we see it.

+

WHAT IF MY POST IS PULLED?

+

You might find that something you posted is no longer on the forum on which it was originally posted. In all likelihood, you posted something that really belongs in either our Classifieds or Lavender Room forum, and you might find your post there (we'll just move your post or thread to the right forum). If your post is pulled entirely, you wrote something inappropriate—at least in our view—and we pulled the post.

+

WHAT IF I'M BANNED?

+

Then you *might* have been (according to us) a very bad boy (or girl). We reserve the right to do this, and at our sole discretion. If you want to keep your account active, be civil. But you might be posting from a location inside a bloc of IP addresses we banned in order to weed out a spammer or bad actor. Email us privately and we'll find out of that's the case. +

MULTIPLE USER NAMES:

+

A big no-no. Stick to one user account. You can change your user name, but no creating multiple accounts. Note that your string of previous user names appends to your user profile.

+

COMMENTS ABOUT RETAILERS OR RACES:

+

Neither complaints nor praises belong on the reader forum. They should be lodged as "comments" attached to the profiles of these business entities, which reside on our databases listing them. You'll find links to these databases on a blue bar across the very top of our reader forum pages. Likewise bike fitters, coaches, and tri clubs.

+

SCIENTIFIC DATA

+

If the data is merely a reprint from another website or publication, all that is needed is a link or a reference to the original source (and as much information is provided by the original publisher). If the data is coming from you, as a private individual, simply do your best to explain your testing protocol and methodology. You should be prepared to answer questions to the best of your ability. If the data is coming from a company that you work for or are otherwise affiliated with, you need to explain your affiliation to that company.

+

ARE THESE ALL THE RULES?

+

No. But these are most of them, in broad strokes. There may be others we place here from time to time. As you become familiar with this forum, and this community, you'll see that we interfere only when we need to. If you take a deep breath before you post, and give your idea a second thought before you push the "send" button, you probably won't write anything with which we'll take umbrage.

+

DISCLAIMER:

+

This is a very loosely moderated forum. We do not and cannot see everything written here, and we may not see an offensive post for awhile or indeed at all unless it is brought to our attention. We are hosting a global digital "water cooler," around which triathletes gather to "talk shop." As such we cannot and do not accept any liability for what others post here. We can simply make our best effort to monitor abuse that is brought to our attention.

+
+ +<%include include_global_js.html%> + diff --git a/site/forum/local/forum_view.html b/site/forum/local/forum_view.html new file mode 100644 index 0000000..709777e --- /dev/null +++ b/site/forum/local/forum_view.html @@ -0,0 +1,212 @@ + +<%~set search_widget = 1%> +<%~set poll_widget = 1%> + + + <%cat_full_name%>: <%forum_name%><%if this_page > 1%>: Page <%this_page%><%endif%> +<%include include_common_head.html%> + + +<%~set hidden_sort = ''%><%set hidden_page = ''%><%set form_sort = ''%><%set form_page = ''%> +<%~if in.sb%><%set hidden_sort .= ";sb=$sb"%><%set form_sort .= ""%><%endif%> +<%~if in.so%><%set hidden_sort .= ";so=$so"%><%set form_sort .= ""%><%endif%> +<%~if in.mh%><%set hidden_page .= ";mh=$mh"%><%set form_page .= ""%><%endif%> +<%~if in.page%><%set hidden_page .= ";page=$this_page"%><%set form_page .= ""%><%endif%> +<%~set do = ''%><%if in.do%><%set do = "do=$this_do;"%><%endif%> + +
+ <%include include_header.html%> + +
+
+ + <%~set content_header_title = $forum_name%> + <%~set btn_new = 1%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + + <%include include_breadcrumb.html%> + + <%~set paging_do = ''%> + <%~if in.do%> + <%~set paging_do = "do=$this_do"%> + <%~endif%> + <%~set pagination_options.type = 'forum'%> + <%~set pagination_options.id = $forum_id%> + <%~set pagination_options.params = "$paging_do$hidden_sort$hidden_page"%> + <%~set pagination_options.page_items = $forum_total_threads%> + +
<%-- start content--%> + <%include include_content_header.html%> +
+ <%include include_hot_topics.html%> + <%include include_quicklaunch.html%> +
+ +
<%-- start table --%> +
+
+
 
+
+ <%~if sb eq post_subject and so ieq asc%><%set url_param_subject_so = 'desc'%><%else%><%set url_param_subject_so = 'asc'%><%endif%> + " title="Sort by Subject"> + Subject + <%~if sb eq post_subject%> <%endif~%> + +
+
+ <%~if sb eq post_username and so ieq asc%><%set url_param_author_so = 'desc'%><%else%><%set url_param_author_so = 'asc'%><%endif%> + " title="Sort by Author"><%~%> + Author + <%~if sb eq post_username%> <%endif~%> + +
+
Views
+
+ <%~if sb eq post_replies and so ieq desc%><%set url_param_replies_so = 'asc'%><%else%><%set url_param_replies_so = 'desc'%><%endif%> + " title="Sort by # of Replies"><%~%> + Replies + <%~if sb eq post_replies%> <%endif~%> + +
+
+ <%~if sb eq post_latest_reply and so ieq asc%><%set url_param_last_post_so = 'desc'%><%else%><%set url_param_last_post_so = 'asc'%><%endif%> + " title="Sort by # of Replies" rel="nofollow"><%~%> + Last Post + <%~if sb eq post_latest_reply%> <%endif~%> + +
+
+
+
+ <%~loop post_loop%> + <%~if post_locked%><%set icon_class = 'locked-thread'%> + <%~elsif post_thread_hot%><%set icon_class = 'hot-thread'%> + <%~elsif post_new and new_replies%><%set icon_class = 'new-replies'%> + <%~elsif post_replies == 1%><%set icon_class = 'first-post'%> + <%~elsif post_replies%><%set icon_class = 'thread'%> + <%~elsif post_new%><%set icon_class = 'new-thread'%> + <%~else%><%set icon_class = 'single-thread'%><%endif%> +
+
+
+ <%~unless post_moved and post_moved_perm < 3%> + + <%~set forum_view = ''%> + <%~if in.do%> + <%~set forum_view = "forum_view=$this_do&"%> + <%~endif%> + <%~if post_moved%><%set url_id = $post_moved_id%><%else%><%set url_id = $post_id%><%endif%> + <%~set url_anchor = ""%> + <%~set url_param_unread = ""%> + <%~if current.user_default_post_display == 1 or not current.user_id%> + <%~if post_depth%> + <%~set url_anchor = "p$post_id"%> + <%~elsif current.user_jump_to_unread%> + <%~set url_param_unread = ";page=unread"%> + <%~set url_anchor = "unread"%> + <%~endif%> + <%~endif%> + <%~endunless%> + + <%~if post_sticky%> Sticky:<%endif%> + <%~if post_moved%> Moved:<%endif%> + + $url_id, params => "$forum_view$hidden_sort$url_param_unread", anchor => $url_anchor)%>" class="title"> + <% post_subject %> + +
+ by + <%~set escaped_username = escape_url $post_username%> + " class="postedby"><%post_username %> + <%if not post_moved%> + | <% post_latest_reply_date %>| <% post_replies || 0 %> replies + <%~if post_depth == 0%> + <%~set url_param_page_last = "post=last-"%> + <%~if post_moved%><%set url_param_page_last .= "$post_moved_id"%><%else%><%set url_param_page_last .= "$post_id"%><%endif%> + <%~set url_anchor = ""%> + <%~if post_display_is_flat%><%set url_anchor = "last"%><%endif%> + $url_anchor)%>" rel="nofollow" class="right-arrow">→ + <%~endif%> + <%~endif%> + + <%-- Server ad on mobile --%> + <%~if row_num == 3 %> + <%~set serve_ad = 1%><%set count = 0%><%set ad_count = 1%> + <%~else%> + <%~set count += 1%><%set show_poll = $count % 25 %><%set show_ad = $count % 10 %> + <%~if not $show_ad %> + <%~set serve_ad = 1%> + <%~if not $show_poll%> +
+ <%include include_poll_widget.html%> + <%~set serve_ad = 0%><%set count = 0%> +
+ <%endif%> + <%endif%> + <%~endif%> + +
+ <%~if $serve_ad%><%set height_choice = $ad_count % 2%> + <%~if not height_choice%><%set ad_height = 250%><%else%><%set ad_height = 600 %><%endif%> + <%~set ad_count += 1%> + <%include include_ad_mobile.html%> + <%~set serve_ad = 0%> + <%~endif%> +
+
+ <%~set include_username_display = $post_username%> + <%include include_username.html%> +
+
<%if not post_moved%><%post_views%><%endif%>
+
<%if not post_moved%><%post_replies%><%endif%>
+
+ <%~if not post_moved%> + <%post_latest_reply_date%> + + <%~if post_replies%> + <%~set escaped_username = escape_url $post_latest_poster%> + by " class="postedby"><%post_latest_poster%> + <%~endif%> + + <%~-- Show jump image for root posts --%> + <%~if post_depth == 0%> + $url_anchor)%>" rel="nofollow" class="right-arrow">→ + <%~endif%> + + <%~endif%> +
+
+ <%~ endloop %> +
+
<%-- end dtable --%> + +
+
+ <%~set paging_do = ''%> + <%~if in.do%> + <%~set paging_do = "do=$this_do"%> + <%~endif%> + <%~set pagination_options.type = 'forum'%> + <%~set pagination_options.id = $forum_id%> + <%~set pagination_options.params = "$paging_do$hidden_sort$hidden_page"%> + <%~set pagination_options.page_items = $forum_total_threads%> + + <%include include_paging.html%> + <%include include_forum_actions.html%> + +
+ <%-- ad for tablet --%> + <%include include_ad_tablet.html%> + +
<%-- end content --%> +
<%-- end contentwrapper --%> +
<%-- end main --%> + <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/forum/local/include_ad.html b/site/forum/local/include_ad.html new file mode 100644 index 0000000..ca60609 --- /dev/null +++ b/site/forum/local/include_ad.html @@ -0,0 +1,72 @@ +<%~if adsize eq '300x250' and $ad_300x250%> + <%~if forum_id eq '4' or page_id eq 'login'%> + <%~if admod eq 'mobile' or $admod eq 'tablet'%> +
+ <%~-- Serve ad for tablet/mobile --%> + +
+ <%~else%> +
+ +
+ <%~endif%> + <%~else%> + <%~if admod eq 'mobile' or $admod eq 'tablet'%> +
+ <%~-- Serve ad for tablet/mobile --%> + +
+ <%~else%> +
+ +
+ <%~endif%> + <%~endif%> +<%~elsif adsize eq '300x600' and $ad_300x600%> + <%~if admod eq 'mobile' or $admod eq 'tablet'%> +
+ <%~-- Serve ad for tablet/mobile --%> + +
+ <%~else%> + <%~if forum_id eq '4' or page_id eq 'login'%> +
+ +
+ <%~else%> +
+ +
+ <%~endif%> + <%~endif%> +<%~elsif adsize eq '728x90' and $ad_728x90%> + <%~if forum_id eq '4' or page_id eq 'login'%> +
+ <%~-- Serve leaderboard ad --%> + +
+ <%~else%> +
+ <%~-- Serve leaderboard ad --%> + +
+ <%~endif%> +<%~endif%> diff --git a/site/forum/local/include_common_head.html b/site/forum/local/include_common_head.html new file mode 100644 index 0000000..7ba6fab --- /dev/null +++ b/site/forum/local/include_common_head.html @@ -0,0 +1,22 @@ +<%~set minify = $config.minify || 0%> +<%~set minify = 0%> +<%~set slowtwitch_forum = 1%> +<%include include_global_head.html%> +<%google_analytics%> + + + + diff --git a/site/forum/local/include_forum_actions.html b/site/forum/local/include_forum_actions.html new file mode 100644 index 0000000..17e58ce --- /dev/null +++ b/site/forum/local/include_forum_actions.html @@ -0,0 +1,35 @@ +
+ <%~if page_id eq 'category_list' and not $current_user_id%> + + + <%elsif page_id eq 'forum_view'%> + All Forums + <%~if btn_new and user_forum_permission >= 5%> + " class="btn default">post new topic + <%~endif%> + + <%~elsif page_id eq 'search_results'%> + All Forums + <%~elsif page_id eq 'user_view'%> + <%~if perm_hide_post%> + Show User's Posts + <%~else%> + Hide User's Posts + <%~endif%> + + <%~if user_id ne $current.user_id%> + <%~set my_username = $current.user_username%> + <%~if user_accept_privmsg == 2 and buddy.$my_username%><%set buddy_ok = 1%><%endif~%> + <%~if user_accept_privmsg == 1 or buddy_ok~%> + " class="btn default">Send Private Message + <%~endif%> + <%~endif%> + + <%~elsif page_id like 'message'%> + <%~if not url_param_mh%><%set url_param_mh = ''%><%endif%> + " title="Private Messages - Inbox" class="btn default">Inbox + " title="Private Messages - Sent" class="btn default">Sent + " title="Private Messages - Compose" class="btn default">Compose + + <%~endif%> +
diff --git a/site/forum/local/include_post_display.html b/site/forum/local/include_post_display.html new file mode 100644 index 0000000..140e9c1 --- /dev/null +++ b/site/forum/local/include_post_display.html @@ -0,0 +1,177 @@ +<%~if hide_post%> +
+
+ Ignored Post +
+ Show This Post +
+
+
+
+
+
+ +
+
+
+
+
+
+
+<%endif%><%-- if hide_post --%> + +<%~if preview%> +
Preview:
+<%~elsif reply_view%> +
Replying to:
+<%~elsif orig_view%> +
Original post:
+<%~endif%> + +
+
+ <%if last%><%endif%><%if first_new%><%endif%> + + <%~if post_deleted%> + Post deleted by <%post_deleted_by%><%if user_perm_moderator%>: <%post_subject%><%endif%> + <%~else%> + <%~if post_moved%>Moved: <%endif%> + <%if post_subject%><%~post_subject%><%else%> <%endif%> + <%~if post_new%> - NEW<%endif%> + <%~endif~%> + + <%if not preview and not hide_post_menu and post_father_id%> + <%~ifnot post_father_on_page%> + <%~set do = ''%> + <%~if in.do%> + <%~set do = "do=$this_do"%> + <%~endif%> + [ $post_father_id, params => "$do$hidden_sort", anchor => "p$post_father_id")%>" rel="nofollow">In reply to ] + <%~else%> + [ In reply to ] + <%~endif%> + <%endif%> + + +
+ <%GForum::GUI::draw( + draw => post_display_options, + edit => "Edit", + delete => "Delete", + cant_post => "Can't Post", + quote => "Quote", + reply => "Reply", + order => "edit,delete,quote,reply", + a_attribs => "", + separator => " | ", + )%> +
+
+ +
+
+
+
+ <%include include_post_display_infobar.html%> +
+
+
+ <%~if not post_deleted or user_perm_moderator%> + <%Slowtwitch::Post::parse_youtube($post_message)%> + <%post_message%> + <%~endif%> +
+ <%~set show_post_notes = 0%><%-- Determine whether or not we need the "post-notes" div --%> + <%~if this_do eq post_preview and in.redo eq post_edit and preview%><%set show_post_notes = 1%> + <%~elsif post_deleted and user_perm_moderator%><%set show_post_notes = 1%> + <%~elsif post_keep and user_perm_moderator%><%set show_post_notes = 1%> + <%~elsif post_moved or post_last_edit_username%><%set show_post_notes = 1%> + <%~endif%> + + <%~if show_post_notes%> +
+ <%~if this_do eq post_preview and in.redo eq post_edit and preview%> +
+ <%~set escaped_username = escape_url unescape_html $current.user_username%> + Last edited by: "><%current.user_username%>: <%GForum::date($TIME)%> +
+ <%~elsif post_last_edit_username%> +
+ <%~set escaped_username = escape_url $post_last_edit_username%> + Last " rel="nofollow">edited by: + "><%post_last_edit_username%>: <%post_last_edit_date%> +
+ <%~endif%> + <%~if post_deleted and user_perm_moderator%> +
+ Deleted by: + <%~set escaped_username = escape_url $post_deleted_by%> + "><%post_deleted_by%><%if post_deleted_time%>: <%post_deleted_date%><%endif%> +
+ <%~endif%> + <%~if post_moved%><%-- An empty pointer to the moved post --%> +
+

Moved:

+ $post_moved_id)%>">Original post +
+ <%~endif%> +
+ <%~endif%> + + <%~set post_attachments_count = 0%> + <%~loop post_attachments%> + <%~if postatt_inline%><%if forum_attachment_inline or not forum_id%><%nextloop%><%endif%><%endif%> + <%~set post_attachments_count += 1%> + <%~endloop%> + <%~if tags.length or post_attachments_count%> +
+ <%~-- /div class="post-footer" --%> +
<%~-- /div class="top-spacer" --%> + <%~endif%> +
+
<%-- drow --%> +
<%-- dtable --%> + +
+ <%GForum::GUI::draw( + draw => post_display_options, + edit => "Edit", + delete => "Delete", + cant_post => "Can't Post", + quote => "Quote", + reply => "Reply", + order => "edit,delete,quote,reply", + a_attribs => "class='btn'", + separator => " ", + )%> +
+
+ +
diff --git a/site/forum/local/include_post_toolbar.html b/site/forum/local/include_post_toolbar.html new file mode 100644 index 0000000..c7a9da2 --- /dev/null +++ b/site/forum/local/include_post_toolbar.html @@ -0,0 +1,33 @@ +<%~set toolbar_id += 1~%> +<%if toolbar_id == 1%> + +<%else%> +
+ <%hidden_form%> +
+
+
+ <%~if num_posts > $mh%> + <%include include_paging.html%> + <%~endif%> +
+
+ <%~set adsize = '728x90'%> + <%include include_ad.html%> +
+
+ +
+<%~endif%> diff --git a/site/forum/local/include_post_write.html b/site/forum/local/include_post_write.html new file mode 100644 index 0000000..f93fa91 --- /dev/null +++ b/site/forum/local/include_post_write.html @@ -0,0 +1,266 @@ +<%~if ie_version >= 5.5 or mozilla_version >= 1.4 or webkit_version >= 312.1%> + <%~if advanced_editor%> + <%~set ADVANCEDEDITOR = 1%> + <%~elsif current.user_advanced_editor and not basic_editor%> + <%~set ADVANCEDEDITOR = 1%> + <%~else%> + <%~set ADVANCEDEDITOR = 0%> + <%~endif%> +<%~else%> + <%~set ADVANCEDEDITOR = 0%> +<%~endif%> +<%~if ADVANCEDEDITOR and not forum_style % 2%> + <%~set ADVANCEDEDITOR = 0%> +<%~endif%> + + + + + +
+
+ +
+
+ + <%~if this_do eq post_edit%> +
+ +
+
+ <%~endif%> + + <%~if current.user_id and config.post_user_custom_username and this_do ne post_edit%> +
+ +
+
+ <%elsif not current.user_id%> + <%~if anonymous.length > 1 or config.post_guest_custom_username%> +
+ +
+ <%~if anonymous.length > 1%> + + <%~else%> + + <%~endif%> + <%~if config.post_guest_custom_username%> + + <%~endif%> +
+
+ <%~endif%><%-- endif anonymous.length > 1 ... --%> + +
+ +
+
+ <%endif%> + +
+ +
+ <%if ADVANCEDEDITOR%><%-- The advanced editor only works for markup posts --%> + + +

<%GForum::language('FORUM_STYLE_MARKUP')%>

+ <%elsif forum_style%><%-- forum_style is set, which means the user gets a choice --%> + + <%~if forum_style % 2%> + + — + Get Markup Help + + <%~if not forum_style_selected % 2%> + + <%~endif%> + <%~endif%> + <%~else%><%-- plain text only --%> + + <%GForum::language('FORUM_STYLE_PLAIN')%> + <%~endif%> +
+
+
+ +
+ +
+ +<%if ADVANCEDEDITOR%> + + + + + + + + +<%else%> + + +<%endif%> +
+
+ +<%if not ADVANCEDEDITOR%> +<%include include_markup_tags.html%> +<%endif%> + +
+ <%if ADVANCEDEDITOR%><%-- The advanced editor only works for markup posts --%> + + + Switch to Basic Editor | + + <%~elsif forum_style % 2%> + Switch to Advanced Editor | + <%endif%> + Check Spelling | + Preview Post +
+ +<%~if current.user_status == 3%> + +<%~endif%> + +<%if can_attach or attachments.length%> +
+ +
+ <%include include_attachments.html%> +
+
+<%endif%> + +
+ +<%if current.user_id%> +
+
+ checked="checked"<%endif%> class="checkbox" tabindex="8" /> + +
+
+ checked="checked"<%endif%> class="checkbox" tabindex="8" /> + +
+ <%~if current.user_forum_permission >= $FORUM_PERM_MODERATOR%> + <%~if root.post_sticky%> +
+ + +
+ <%~else%> +
+ checked="checked"<%endif%> class="checkbox" tabindex="8" /> + +
+ <%~endif%> + <%~if root.post_locked%> +
+ + +
+ <%~else%> +
+ checked="checked"<%endif%> class="checkbox" tabindex="8" /> + +
+ <%~endif%> + <%~endif%> +
+<%endif%> + +
+ <%include markup_help.html%> +
diff --git a/site/forum/local/include_quicklaunch.html b/site/forum/local/include_quicklaunch.html new file mode 100644 index 0000000..95729a7 --- /dev/null +++ b/site/forum/local/include_quicklaunch.html @@ -0,0 +1,9 @@ + diff --git a/site/forum/local/include_sidebar.html b/site/forum/local/include_sidebar.html new file mode 100644 index 0000000..6958594 --- /dev/null +++ b/site/forum/local/include_sidebar.html @@ -0,0 +1,91 @@ +<%~if page_id eq 'category_list'%> + +<%~elsif search_widget or $forum_id%> +<%include include_sidebar_search.html%> +<%~endif%> + +<%~if page_id ne 'category_list' and $poll_widget%> +<%~set adsize = '300x250'%> +<%include include_ad.html%> + +<%~if poll_widget or $forum_id%> +<%include include_poll_widget.html%> +<%~endif%> +<%~endif%> + +<%~if page_id eq 'category_list'%> +<%~set adsize = '300x600'%> +<%include include_ad.html%> +<%~else%> +
+
+
+
+
+ + + + + +
+
+
+<%~if $forum_id != 4 %> +<%~set adsize = '300x600'%> +<%include include_ad.html%> +<%~endif%> +
+ + + +<%~endif%> + +<%~if $forum_id == 11 %> +<%ezoic_desktop_sidebar%> +<%~endif%> + + + + diff --git a/site/forum/local/lost_password_enter_username.html b/site/forum/local/lost_password_enter_username.html new file mode 100644 index 0000000..0424d66 --- /dev/null +++ b/site/forum/local/lost_password_enter_username.html @@ -0,0 +1,85 @@ + + + + <%cat_full_name%>: Lost Password + <%include include_common_head.html%> + + +<%~set content_header_title = 'Lost Password'%> + + +
+ <%include include_header.html%> +
+
+ + + <%~set content_header_title = 'Lost Password'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = 'Lost Password'%> + + <%include include_breadcrumb.html%> + +
<%-- start content--%> + <%include include_content_header.html%> + <%if form.page_errors%>

Enter your Username

<%endif%> + +

+ If you have lost your password, <%cat_full_name%> can generate a new temporary + password for you to log in with. Note that after logging in the temporary + password will be lost so you should change your password immediately upon + logging in with the temporary password. The temporary password will be sent + to you at the e-mail address you specified when signing up. +

+ +
+ <%hidden_form%> + + + +
+
+
Your Username *
+
+
+ +
+
+ <%~set captcha_active = Plugins::GForum::GoogleCaptcha::is_captcha_enabled()%> + <%~if captcha_active%> +
+
Prove you're not a robot
+
+
+ <%Plugins::GForum::GoogleCaptcha::get_rcaptcha_image()%> +
+
+ <%endif%> +
+
+
+
+ +

* The temporary password will be sent to the e-mail address you entered while registering.

+
+
+
+
+ +
+ +
<%-- end content --%> +
+ + <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + + diff --git a/site/forum/local/post_view_flat.html b/site/forum/local/post_view_flat.html new file mode 100644 index 0000000..21f3ecc --- /dev/null +++ b/site/forum/local/post_view_flat.html @@ -0,0 +1,158 @@ + +<%~set search_widget = 1%> +<%~set poll_widget = 1%> + + + <%root_post_subject%><%if this_page > 1%> (Page <%this_page%>)<%endif%>: <%forum_name%>: <%cat_full_name%> + <%include include_common_head.html%> + <%~if this_do eq post_view_printable%> + + <%~endif%> + + + +<%~set hidden_sort = ''%><%set hidden_page = ''%> +<%~if in.sb%><%set hidden_sort .= ";sb=$sb"%><%endif%> +<%~if in.so%><%set hidden_sort .= ";so=$so"%><%endif%> +<%~if in.mh%><%set hidden_page .= ";mh=$mh"%><%endif%> +<%~if in.page%><%set hidden_page .= ";page=$this_page"%><%endif%> + + +
+ <%include include_header.html%> + +
+
+ + + <%~set content_header_title = $forum_name%> + <%~set post_toolbar = 1%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + <%~set breadcrumbs.1.name = $root_post_subject%> + + <%include include_breadcrumb.html%> +
<%-- start content--%> + <%include include_content_header.html%> + +<%~set root_post_id ||= $post_root_id || $post_id%> +<%~set do = ''%> +<%~if in.do%> + <%~set do = "do=$this_do"%> +<%~endif%> +<%~set pagination_options.type = 'post'%> +<%~set pagination_options.id = $root_post_id%> +<%~set pagination_options.params = "$do$hidden_sort$hidden_page"%> +<%~set pagination_options.page_items = $num_posts%> +<%~set pagination_options.show_view_all = 1%> + +
+ <%include include_paging.html%> + <%include include_quicklaunch.html%> +
+ + <%loop post_loop%> + <%~if is_moderator and post_reports_count%><%set admin_show_post = 1%><%else%><%set admin_show_post = 0%><%endif%> + <%~if blocked and not admin_show_post and in.post != $post_id%><%set hide_post = 1%><%else%><%set hide_post = 0%><%endif%> + + <%-- Serve ad on mobile --%> + <%~if row_num == 1%> + <%~set serve_ad = 1%><%set ad_count = 1%> + <%~else%> + <%~set count += 1%><%set odd = $count % 10%> + <%~if not $odd%><%set serve_ad = 1%><%set count = 0%><%endif%> + <%~endif%> + + <%include include_post_display.html%> + +<%~if forum_id eq '4' %> +<%~else %> + <%~if $serve_ad%><%set height_choice = $ad_count % 2%> + <%~if not height_choice%><%set ad_height = 250%><%else%><%set ad_height = 600 %><%endif%> + <%~set ad_count += 1%> + <%include include_ad_mobile.html%> + <%~set serve_ad = 0%><%set count = 0%> + <%~endif%> +<%~if not $odd%> + +<%~set adsize = '728x90'%> <%include include_ad.html%> + + +<%~endif%> +<%~endif%> + + <%endloop%> + + <%include include_post_toolbar.html%> + + <%-- ad for tablet --%> + <%~if num_posts > 3%> + <%include include_ad_tablet.html%> + <%~endif%> + +
<%-- end content --%> +
<%-- end contentwrapper --%> +
<%-- end main --%> + + <%include include_footer.html%> +
+ +<%if spellcheck%><%spellcheck_head%><%endif%> +<%include include_global_js.html%> + + diff --git a/site/forum/local/redirect.html b/site/forum/local/redirect.html new file mode 100644 index 0000000..ef7f036 --- /dev/null +++ b/site/forum/local/redirect.html @@ -0,0 +1,40 @@ + + +<:: Slowtwitch Forums ::> +<%include include_common_head.html%> + + +<%~set content_header_title = 'Redirect'%> +<%~set content_header_subtitle = 'You are being redirected'%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> + +
+ <%include include_header.html%> +
+
+ + + <%~set content_header_title = 'Forum'%> + <%include include_breadcrumb.html%> + +
<%-- start content--%> + <%include include_content_header.html%> + +

+ You are leaving Slowtwitch to the selected URL, <%url%>. +

+ +
<%-- end content --%> +
+
+ <%include include_footer.html%> +
+ +<%include include_global_js.html%> + diff --git a/site/forum/local/user_signup.html b/site/forum/local/user_signup.html new file mode 100644 index 0000000..aba7582 --- /dev/null +++ b/site/forum/local/user_signup.html @@ -0,0 +1,158 @@ + + + + <%cat_full_name%>: Sign Up +<%include include_common_head.html%> + + + +
+ <%include include_header.html%> + +
+
+ + + <%~set content_header_title = 'Sign Up'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
<%-- start content--%> + <%include include_content_header.html%> + +

You can sign up to the forum by entering your information here.

+ <%GForum::Config::tpl_get(require_signup_email_validation)%> + <%if require_signup_email_validation%> +

If you have already filled out the signup form but still have not received a validation e-mail, click here to have the validation e-mail re-sent.

+ <%endif%> + +
+ + <%hidden_form%> + +
+
+
Username *
+
+
+ +
+
+
+
Password *
+
+
+ +
Password Strength: None
+
+
+
+
Confirm Password *
+
+
+ +
+
+
+
E-mail Address *
+
+
+ +
Used for admin purposes only. We will never make this public.
+
+
+
+
Country *
+
+
+ <%~set countries = Plugins::GForum::ForumTrainingRegister::country_codes%> + +
+
+
+
Sex *
+
+
+ +
+
+
+
Units of Measure *
+
+
+ +
+
+
+
+ As a mischief-control tactic, name a specific RACE (be specific!) associated with the sport THIS site covers that interests you* +
+
+
+ +
+
+ <%~if 0%> +
+
Would you like to receive our weekly newsletter?
+
+
+ +
+
+ <%endif%> + <%~set captcha_active = Plugins::GForum::GoogleCaptcha::is_captcha_enabled()%> + <%~if captcha_active%> +
+
Prove you're not a robot
+
+
+ <%Plugins::GForum::GoogleCaptcha::get_rcaptcha_image()%> +
+
+ <%endif%> +
+
+
+
+ +

Note: Fields marked with * are required for validation

+
+
+
+
+ +
<%-- end content --%> +
+
+ + <%include include_footer.html%> + +
+ +<%~include include_global_js.html%> + + diff --git a/site/forum/login.html b/site/forum/login.html new file mode 100644 index 0000000..be36321 --- /dev/null +++ b/site/forum/login.html @@ -0,0 +1,136 @@ + +<%~get_cgi_value('from_url')%> +<%~get_cgi_value('from')%> +<% if $cgi_from_url%> + <%~set url_str = $cgi_from_url%> +<% elsif $url and not $cgi_from%> + <%~set url_str = $url%> +<% else %> + <%if $cgi_from eq 'racecalendar'%> + <% set url_str = "http://calendar.slowtwitch.com/" %> + <%elsif $cgi_from eq 'retailers'%> + <% set url_str = "http://www.slowtwitch.com/retailers" %> + <%elsif $cgi_from eq 'triclubs'%> + <% set url_str = "http://www.slowtwitch.com/triclubs" %> + <%elsif $cgi_from eq 'runshops'%> + <% set url_str = "http://www.slowtwitch.com/runshops" %> + <%elsif $cgi_from eq 'coaches'%> + <% set url_str = "http://www.slowtwitch.com/coaches" %> + <%elsif $cgi_from eq 'fitters'%> + <% set url_str = "http://www.slowtwitch.com/fitters" %> + <%elsif $cgi_from eq 'survey'%> + <% set url_str = "http://forum.slowtwitch.com/survey/?from=gforum" %> + <%elsif $cgi_from eq 'training'%> + <% set url_str = "http://training.slowtwitch.com/?from=gforum" %> + <%elsif $cgi_from eq 'protri'%> + <% set url_str = "http://www.slowtwitch.com/protri/?from=gforum" %> + <%elsif $cgi_from eq 'stackreach'%> + <% set url_str = "http://www.slowtwitch.com/stackreach/index.php?from=gforum" %> + <%elsif $from and $from ne '' and not $from contains 'do=logout' %> + <% set url_str = $$from %> + <%else%> + <%set self_url = get_self_url() %> + <%if self_url and not self_url contains 'do=login' and not self_url contains 'do=logout'%> + <% set url_str = $self_url %> + <%else%> + <%set url_str = GForum::SEO::url(forum => 1) %> + <%endif%> + <%endif%> +<%endif%> + + + <%cat_full_name%>: Login + <%~if current_user_id%> + + <%~endif%> + <%include include_common_head.html%> + + +
+ <%include include_header.html%> + +
+
+ + + <%~set content_header_title = 'Login'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = 'Login'%> + + <%include include_breadcrumb.html%> + +
<%-- start content--%> + <%include include_content_header.html%> +
+ <%hidden_form%> + + +
+
+
+
+
Username
+
+
+ value="<%user_username%>"<%endif%> class="txt" /> +
+
+
+
Password
+
+
+ +
+
+
+
Remember me
+
+
+ checked="checked"<%endif%> /> + +
+
+
+
Don't use cookies
+
+
+ checked="checked"<%endif%> /> +
+
+
+
+
+ +
+
+
+
+
+ +
+
+ +
+ +
<%-- end content --%> +
+
+ <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/forum/lost_password_enter_username.html b/site/forum/lost_password_enter_username.html new file mode 100644 index 0000000..7dcd536 --- /dev/null +++ b/site/forum/lost_password_enter_username.html @@ -0,0 +1,75 @@ + + + + <%cat_full_name%>: Lost Password + <%include include_common_head.html%> + + +<%~set content_header_title = 'Lost Password'%> + + +
+ <%include include_header.html%> +
+
+ + + <%~set content_header_title = 'Lost Password'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = 'Lost Password'%> + + <%include include_breadcrumb.html%> + +
<%-- start content--%> + <%include include_content_header.html%> + <%if form.page_errors%>

Enter your Username

<%endif%> + +

+ If you have lost your password, <%cat_full_name%> can generate a new temporary + password for you to log in with. Note that after logging in the temporary + password will be lost so you should change your password immediately upon + logging in with the temporary password. The temporary password will be sent + to you at the e-mail address you specified when signing up. +

+ +
+ <%hidden_form%> + + + +
+
+
Your Username *
+
+
+ +
+
+
+
+
+
+ +

* The temporary password will be sent to the e-mail address you entered while registering.

+
+
+
+
+ +
+ +
<%-- end content --%> +
+ + <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + + diff --git a/site/forum/lost_password_sent.html b/site/forum/lost_password_sent.html new file mode 100644 index 0000000..9d93e5f --- /dev/null +++ b/site/forum/lost_password_sent.html @@ -0,0 +1,42 @@ + + + <%cat_full_name%>: Lost Password Sent + <%include include_common_head.html%> + +<%~set content_header_title = 'Temporary Password Sent'%> + + +
+ <%include include_header.html%> + +
+
+ + + <%~set content_header_title = 'Lost Password'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = 'Lost Password'%> + + <%include include_breadcrumb.html%> + +
<%-- start content--%> + <%include include_content_header.html%> + +

+ A temporary password has been e-mailed to you. Once you receive it, you can log in + using it from ">the login page. +

+

+ Remember that this password is good for one login only; after logging + in you should change your password from the profile page. +

+
<%-- end content --%> +
+ + <%include include_footer.html%> +
+ +<%~include include_global_js.html%> + diff --git a/site/forum/markup_help.html b/site/forum/markup_help.html new file mode 100644 index 0000000..0f98320 --- /dev/null +++ b/site/forum/markup_help.html @@ -0,0 +1,95 @@ +<%~GForum::Markup::list_tags%><%--loads 'markup_loop' variable--%> +<%~set sample_text = 'Sample Text'%> +
+<%~loop markup_loop%> +
+<%~unless has_args%><%-- Unfortunately, dynamically displaying markup that takes arguments cannot be easily done --%> +
+ <%~if has_closing%> + [<%tag%>]<%if tag eq ul or tag eq ol%>[li]<%endif%><%sample_text%><%if tag eq ul or tag eq ol%>[/li]<%endif%>[/<%tag%>] + <%~else%> + [<%tag%>] + <%~endif%> +
+
+ <%~if has_closing%> + <%html%><%if tag eq ul or tag eq ol%>
  • <%endif%><%sample_text%><%if tag eq ul or tag eq ol%>
  • <%endif%><%closing%> + <%~else%> + <%html%> + <%~endif%> +
    +<%~endunless%> +
    +<%~endloop%> + +<%~if config.markup_allow_custom_color%> +
    +
    + [#008000]<%sample_text%>[/#], + [#789abc]<%sample_text%>[/#], + [#707070]<%sample_text%>[/#], etc. +
    +
    + <%sample_text%>, + <%sample_text%>, + <%sample_text%>, etc. +
    +
    +<%~endif%> +<%~if config.markup_allow_font%> +
    +
    + [font "Times New Roman"]<%sample_text%>[/font] +
    +
    + <%sample_text%> +
    +
    +<%~endif%> +<%~if config.markup_allow_size%> +
    +
    + [size 6]<%sample_text%>[/size] +
    +
    + <%sample_text%> +
    +
    +<%~endif%> +<%~if config.markup_allow_image%> +
    +
    + [image]/images/powered.gif[/image] +
    +
    + +
    +
    +<%~endif%> +
    +
    + [inline attachment_filename.gif] +
    +
    + +
    +
    +<%~if config.markup_allow_url%> +
    +
    +
      +
    1. [url]http://www.gossamer-threads.com[/url]
    2. +
    3. [url "http://www.gossamer-threads.com"]Gossamer Threads[/url]
    4. +
    5. [email]gforum@gossamer-threads.com[/email]
    6. +
    +
    + +
    +<%~endif%> +
    diff --git a/site/forum/message.html b/site/forum/message.html new file mode 100644 index 0000000..9192ecc --- /dev/null +++ b/site/forum/message.html @@ -0,0 +1,93 @@ + + + + <%cat_full_name%>: Send message +<%include include_common_head.html%> + + +<%~if bad_username%> + <%~if user_username%> + <%~set error = "No such user: $user_username"%> + <%~else%> + <%~set error = "No recipient entered!"%> + <%~endif%> +<%~elsif user_no_privmsg%> + <%~set error = "$user_username has chosen not to accept private messages"%> +<%~endif%> +<%~set content_header_title = 'Send Private Message'%> +<%~set url = GForum::SEO::url(params => "do=message_list")%> +<%~set content_header_breadcrumb = "Messages"%> +<%~if this_do eq message_reply_write%> +<%~set content_header_subtitle = "Send a Reply to $user_username"%> +<%~endif%> + + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = 'New Message'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = 'Private Messages'%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "do=message_list")%> + <%~set breadcrumbs.1.name = 'New Message'%> + + <%~set url_param_mh = ""%> + <%~if in.mh%><%set url_param_mh = ";mh=$mh"%><%endif%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + + <%~if this_do eq message_reply_write and not user_accept_privmsg%> +

    + <%user_username%> has chosen not to accept private messages. +

    + <%~else%> + <%preview || ''%> + +
    + <%hidden_form%> + <%~if this_do eq message_reply_write%> + + + <%~endif%> + + <%include include_message_write.html%> + +
    + <%~if this_do eq message_reply_write%> + + <%~else%> + + <%~endif%> +
    +
    + +
    + <%~if this_do eq message_reply_write%> + <%~parent%> + <%~set preview = 0%><%set reply_view = 1%> + <%include include_message_display.html%> + <%~endif%> + <%~endif%> + +
    <%-- end content --%> +
    +
    +
    + + <%include include_footer.html%> + +
    + + +<%~include include_global_js.html%> + diff --git a/site/forum/message_delete_all_confirm.html b/site/forum/message_delete_all_confirm.html new file mode 100644 index 0000000..e58defe --- /dev/null +++ b/site/forum/message_delete_all_confirm.html @@ -0,0 +1,60 @@ + + + + <%cat_full_name%>: Messages: Delete All + <%include include_common_head.html%> + + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = "Delete All Messages"%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = 'Private Messages'%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "do=message_list")%> + <%~set breadcrumbs.1.name = 'Delete All Messages'%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + +
    + <%hidden_form%> + + +
    +

    + You have chosen to delete all your <%if in.skip_recd%>sent<%else%>received<%endif%> + private messages. Note that messages which you have selected to keep + <%unless in.skip_recd%>and any unread messages <%endunless%>will not be deleted. +

    + +

    + Are you sure you wish to permanently delete + <%if in.skip_recd%><%sent_messages_to_delete%> sent message<%if sent_messages_to_delete != 1%>s<%endif%>? + <%~else%><%messages_to_delete%> received message<%if messages_to_delete != 1%>s<%endif%>? + <%~endif%> +

    +
    + + +
    + +
    <%-- end content --%> +
    +
    +
    + + <%include include_footer.html%> + + +<%~include include_global_js.html%> + diff --git a/site/forum/message_list.html b/site/forum/message_list.html new file mode 100644 index 0000000..56e2d78 --- /dev/null +++ b/site/forum/message_list.html @@ -0,0 +1,188 @@ + +<%~if in.mode and in.mode eq sent%><%set sent_mode = 1%><%endif%> + + + <%cat_full_name%>: <%if search%> Search <%endif%>Messages +<%include include_common_head.html%> + + +<%~set content_header_title = 'Private Messages'%> + +<%~if sent_mode and not $search%> + <%~GForum::Message::sent_messages%> +<%~endif%> + +<%~set hidden_sort = ''%><%set hidden_page = ''%> +<%~if in.sb%><%set hidden_sort .= ";sb=$sb"%><%endif%> +<%~if in.so%><%set hidden_sort .= ";so=$so"%><%endif%> +<%~if in.sent_sb%><%set hidden_sort .= ";sent_sb=$sent_sb"%><%endif%> +<%~if in.sent_so%><%set hidden_sort .= ";sent_so=$sent_so"%><%endif%> +<%~if sent_mode%><%set hidden_page .= ";mode=sent"%><%endif%> +<%~if in.mh%><%set hidden_page .= ";mh=$mh"%><%endif%> + +<%~if sent_mode%> + <%~set sb = $sent_sb%> + <%~set so = $sent_so%> +<%~endif%> + + +
    + <%include include_header.html%> + +
    +
    + + <%~set content_header_title = "Private Messages"%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = $content_header_title%> + + <%~if sent_mode%> + <%~set content_header_title .= ': Sent'%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "do=message_list")%> + <%~init hash breadcrumbs.1%> + <%~set breadcrumbs.1.name = 'Private Messages: Sent'%> + <%~else%> + <%~set content_header_title .= ': Inbox'%> + <%~endif%> + + + <%~set url_param_mh = ""%> + <%~if in.mh%><%set url_param_mh = ";mh=$mh"%><%endif%> + + <%~if sent_num_deleted or $num_deleted%><%set success = "Selected messages were deleted"%><%endif%> + + <%include include_breadcrumb.html%> +
    <%-- start content--%> + <%include include_content_header.html%> +

    + <%if sent_mode%> + You have <%sent_num_messages || 0%> sent message<%if sent_num_messages != 1%>s<%endunless%>. + <%else%> + You have <%num_messages%> message<%if num_messages != 1%>s<%endif%> — + <%num_new%> new, + <%num_read + $num_replied%> read, + <%num_replied%> replied. + <%endif%> +

    + +
    + <%hidden_form%> + <%~if in.mh%> + + <%~endif%> + <%~if sent_mode%> + + <%~if in.sent_so%> + + <%~endif%> + <%~if in.sent_sb%> + + <%~endif%> + <%~else%> + <%~if in.so%> + + <%~endif%> + <%~if in.sb%> + + <%~endif%> + <%~endif%> + + <%~set url_param_sent_mode = ""%> + <%~if sent_mode%><%set url_param_sent_mode = "sent_"%><%endif%> + +
    + +
    + + for + + + <%if search%><%endif%> +
    +
    + +
    +
    +
    +
     
    +
    + <%~unless sent_mode%> + <%~if sb eq msg_status and so ieq asc%><%set url_param_so = "desc"%><%else%><%set url_param_so = "asc"%><%endif%> + " title="Sort by New/Read/Replied"><%~%> + Status + <%~if sb eq msg_status%> <%endif~%> + + <%endunless~%> +
    +
    + <%~if sb eq msg_subject and so ieq asc%><%set url_param_so = "desc"%><%else%><%set url_param_so = "asc"%><%endif%> + " title="Sort by Subject"><%~%> + Subject + <%~if sb eq msg_subject%> <%endif~%> + +
    +
    + <%~if sb eq msg_username and so ieq asc%><%set url_param_so = "desc"%><%else%><%set url_param_so = "asc"%><%endif%> + " title="Sort by Author"><%~%> + <%if sent_mode%>To<%else%>From<%endif%> + <%~if sb eq msg_username%> <%endif~%> + +
    +
    + <%~if sb eq msg_time and so ieq desc%><%set url_param_so = "asc"%><%else%><%set url_param_so = "desc"%><%endif%> + " title="Sort by date sent"><%~%> + Sent + <%~if sb eq msg_time%> <%endif~%> + +
    +
    +
    <%-- end dhead --%> + + <%~if sent_mode%> + <%include include_message_list_sent.html%> + <%~else%> + <%include include_message_list.html%> + <%~endif%> +
    <%-- end dtable --%> + + <%~set pagination_options.params = "do=$this_do$hidden_sort$hidden_page"%> + <%if sent_mode%> + <%~set pagination_options.page_items = $sent_num_messages%> + <%~set pagination_options.param_page = 'sent_page'%> + <%else%> + <%~set pagination_options.page_items = $num_messages%> + <%endif%> + + <%~if $pagination_options.page_items%> +
    +
    + <%include include_paging.html%> +
    + +
    +
    + <%~endif%> + +
    + +
    <%-- end content --%> +
    <%-- end contentwrapper --%> +
    <%-- end main --%> + + <%include include_footer.html%> +
    + + +<%~include include_global_js.html%> + + diff --git a/site/forum/message_reply_write.html b/site/forum/message_reply_write.html new file mode 100644 index 0000000..d5ac008 --- /dev/null +++ b/site/forum/message_reply_write.html @@ -0,0 +1,4 @@ +<%~-- +This page is displayed when you reply to a private message. +--%> +<%include message.html%> diff --git a/site/forum/message_send.html b/site/forum/message_send.html new file mode 100644 index 0000000..77931ba --- /dev/null +++ b/site/forum/message_send.html @@ -0,0 +1,51 @@ + + + + <%cat_full_name%>: Message sent! + "query")%>" /> + <%include include_common_head.html%> + + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = "Message to $to_user_username Sent!"%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = 'Private Messages'%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "do=message_list")%> + <%~set breadcrumbs.1.name = 'New Message Sent'%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + + <%~if attachment_errors%> +

    + <%attachment_errors%> +

    + <%~endif%> + +

    + Your message to <%to_user_username%> has been sent. You will be automatically returned to your message list shortly. +

    +

    + ">Return to your message list +

    + +
    <%-- end content --%> +
    +
    + <%include include_footer.html%> +
    + + +<%~include include_global_js.html%> + diff --git a/site/forum/message_sent_view.html b/site/forum/message_sent_view.html new file mode 100644 index 0000000..967d128 --- /dev/null +++ b/site/forum/message_sent_view.html @@ -0,0 +1,2 @@ +<%set sent_message = 1%> +<%~include message_view.html~%> diff --git a/site/forum/message_view.html b/site/forum/message_view.html new file mode 100644 index 0000000..76bc7c9 --- /dev/null +++ b/site/forum/message_view.html @@ -0,0 +1,67 @@ + + + + <%cat_full_name%>: <%if sent_message%>Sent <%endif%>Message: <%msg_subject%> + <%include include_common_head.html%> + + + +<%~if sent_message%> +<%~set content_header_title .= "Sent Message: $msg_subject"%> +<%~set url = GForum::SEO::url(params => "do=message_list;mode=sent")%> +<%~set content_header_breadcrumb = "Sent Messages"%> +<%~else%> +<%~set content_header_title .= "Message: $msg_subject"%> +<%~set url = GForum::SEO::url(params => "do=message_list")%> +<%~set content_header_breadcrumb = "Messages"%> +<%~endif%> + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = $msg_subject%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = 'Private Messages'%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "do=message_list")%> + <%~set breadcrumbs.1.name = $msg_subject%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + +
    + <%hidden_form%> + + <%include include_message_display.html%> +
    + +
    <%-- end content --%> +
    +
    + <%include include_footer.html%> +
    + +<%~include include_global_js.html%> + diff --git a/site/forum/permission_denied.html b/site/forum/permission_denied.html new file mode 100644 index 0000000..e055452 --- /dev/null +++ b/site/forum/permission_denied.html @@ -0,0 +1,47 @@ + + + + <%cat_full_name%>: Permission Denied + <%include include_common_head.html%> + + +<%~set content_header_title = 'Permission Denied'%> + + +
    + <%include include_header.html%> + +
    + <%~set content_header_title = $user_username%> + + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = "Permission Denied!"%> + +
    + + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + +

    Oops! You are not authorized to view that page<%if permission_denied_reason%>:<%else%>.<%endif%>

    + + <%~if permission_denied_reason%> +

    + <%permission_denied_reason%> +

    + <%~endif%> + +
    <%-- end content --%> +
    + +
    + <%include include_footer.html%> +
    + + +<%~include include_global_js.html%> + diff --git a/site/forum/personal_race_add.html b/site/forum/personal_race_add.html new file mode 100644 index 0000000..a863975 --- /dev/null +++ b/site/forum/personal_race_add.html @@ -0,0 +1,109 @@ + + + + <%cat_full_name%>: Add a Personal Race + <%include include_common_head.html%> + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = 'Add A Personal Race'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = 'User Profile'%> + <%~set escaped_username = escape_url unescape_html $current_user_username%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "username=$escaped_username")%> + <%~set breadcrumbs.1.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + +
    + + + <%hidden_form%> + +
    +
    +
    Name
    +
    +
    + +
    +
    +
    +
    URL
    +
    +
    + +
    +
    +
    +
    City
    +
    +
    + +
    +
    +
    +
    State
    +
    +
    + +
    +
    +
    +
    Country
    +
    +
    + +
    +
    +
    +
    Date
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + +
    +
    +
    + +
    + +
    <%-- end content --%> + +
    + + <%include include_footer.html%> +
    + +<%~include include_global_js.html%> + + diff --git a/site/forum/personal_race_edit.html b/site/forum/personal_race_edit.html new file mode 100644 index 0000000..adde1e1 --- /dev/null +++ b/site/forum/personal_race_edit.html @@ -0,0 +1,117 @@ + + + + <%cat_full_name%>: Edit a Personal Race + <%include include_common_head.html%> + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = 'Edit A Personal Race'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = 'User Profile'%> + <%~set breadcrumbs.0.url = GForum::SEO::url(type => "profile", id => $current_user_username)%> + <%~set breadcrumbs.1.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + +
    + + + + <%hidden_form%> + +
    +
    +
    Name
    +
    +
    + +
    +
    +
    +
    URL
    +
    +
    + +
    +
    +
    +
    City
    +
    +
    + +
    +
    +
    +
    State
    +
    +
    + +
    +
    +
    +
    Country
    +
    +
    + +
    +
    +
    +
    Date
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    +
    + +
    +
    +
    + +
    + +
    <%-- end content --%> + +
    + + <%include include_footer.html%> +
    + + <%~include include_global_js.html%> + + diff --git a/site/forum/poll.html b/site/forum/poll.html new file mode 100644 index 0000000..8b703ea --- /dev/null +++ b/site/forum/poll.html @@ -0,0 +1 @@ +<%include include_poll_widget.html%> diff --git a/site/forum/poll_success.html b/site/forum/poll_success.html new file mode 100644 index 0000000..591aaf5 --- /dev/null +++ b/site/forum/poll_success.html @@ -0,0 +1,13 @@ +<%~if error%> +

    +<%~if error eq 'POLL_VOTE_EMPTY'%>Error recording vote. +<%~elsif error eq 'POLL_DOES_NOT_EXIST'%>No such poll/quiz exists. +<%~elsif error eq 'POLL_DISABLED'%>Sorry, it is enabled. +<%~elsif error eq 'POLL_ALREADY_VOTED'%>Sorry, you have already voted once. +<%~endif%> +

    +

    Click <%endif%>">here to go back to the poll

    +<%~else%> +Your vote has been recorded! +

    Click <%endif%>">here to see the poll results

    +<%~endif%> diff --git a/site/forum/post_already_posted.html b/site/forum/post_already_posted.html new file mode 100644 index 0000000..87e365c --- /dev/null +++ b/site/forum/post_already_posted.html @@ -0,0 +1,62 @@ + +<%~set search_widget = 1%> + + + <%cat_full_name%>: Message already posted + $forum_id, param_type => "query")%>" /> +<%include include_common_head.html%> + + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = 'Message already posted'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + <%~set breadcrumbs.1.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> +

    + You attempted to post a message more than once. Your post has already + been posted in <%cat_full_name%>: <%forum_name%>. You will be + automatically redirected to the forum shortly. +

    + +

    + <%~set url_anchor = ""%> + <%~if not current.user_id or current.user_default_post_display%><%set url_anchor = "p$post_id"%><%endif%> + $post_id, anchor => $url_anchor)%>">Click here to view the post + or choose the forum you wish to visit: +

    + +
    +
    + <%hidden_form%> + + +
    +
    + +
    <%-- end content --%> +
    +
    + + <%include include_footer.html%> + +
    + +<%~include include_global_js.html%> + diff --git a/site/forum/post_delete.html b/site/forum/post_delete.html new file mode 100644 index 0000000..2de4690 --- /dev/null +++ b/site/forum/post_delete.html @@ -0,0 +1,43 @@ + +<%~set search_widget = 1%> + + + <%cat_full_name%>: Post Deleted! +<%include include_common_head.html%> + + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = 'Post Deleted'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + <%~set breadcrumbs.1.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> +

    + Your message<%if num_replies%>, and <%num_replies%> repl<%if num_replies == 1%>y<%else%>ies<%endif%>, have<%else%> has<%endif%> + been deleted. $forum_id)%>">Return to the forum +

    + +
    <%-- end content --%> +
    +
    + + <%include include_footer.html%> + +
    + +<%~include include_global_js.html%> + diff --git a/site/forum/post_delete_confirm.html b/site/forum/post_delete_confirm.html new file mode 100644 index 0000000..c602e14 --- /dev/null +++ b/site/forum/post_delete_confirm.html @@ -0,0 +1,78 @@ +<%~-- +This page is displayed when you click delete - it serves as a delete +confirmation page. +--%> +<%include include_common_head.html%> +<%~GForum::Post::View::get($in.post)%> +<%~set post_dne = 0%> +<%~if not post_id%><%set post_dne = 1%> +<%~elsif post_moved and this_do ne post_delete_moved_confirm%><%set post_dne = 1%> +<%~endif%> +<%~if post_dne%><%set error = GForum::language('POST_DOES_NOT_EXIST')%><%include error.html%><%endparse%><%endunless~%> +<%~set search_widget = 1%> + + + + <%cat_full_name%>: Confirm delete +<%include include_common_head.html%> + + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = 'Confirm deletion'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + <%~set breadcrumbs.1.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + +

    You have chosen to delete the following post:

    + <%~if post_moved%> +

    Note that since this is a moved post, you are only deleting the move record, not the original post.

    + <%~endif%> + + <%~set hide_post_menu = 1%> + <%~set tags=GForum::Tags::get_tags_from_post($post_id)%> + <%include include_post_display.html%> + +
    + +
    +
    + <%hidden_form%> + <%~if post_moved%> + + <%~else%> + <%~if user_forum_permission >= 6%> + + <%~if post_replies%> + + <%~endif%> + <%~endif%> + + <%~endif%> +
    +
    + +
    <%-- end content --%> +
    +
    + + <%include include_footer.html%> + +
    + +<%~include include_global_js.html%> + diff --git a/site/forum/post_detach_select.html b/site/forum/post_detach_select.html new file mode 100644 index 0000000..a34fccb --- /dev/null +++ b/site/forum/post_detach_select.html @@ -0,0 +1,83 @@ +<%~GForum::Post::View::get($in.post_id)%> +<%~unless post_id%><%set error = GForum::language('POST_DOES_NOT_EXIST')%><%include error.html%><%endparse%><%endunless~%> +<%~set search_widget = 1%> + + + + <%cat_full_name%>: <%forum_name%>: <%if post_deleted%>Post deleted by <%post_deleted_by%>: <%endif%><%post_subject%>: Detach & Move Thread +<%include include_common_head.html%> + + + +<%~set content_header_title = 'Detach & Move Thread'%> +<%~set content_header_subtitle = 'This post and any replies will be made into a new thread.'%> +<%~set show_subject_crumb = 1%> + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = 'Detach & Move Post'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + <%~set breadcrumbs.1.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> +

    + You have chosen to detach the post ' $post_id, anchor => "p$post_id")%>"><%post_subject%>' + <%~if post_replies%> and its <%post_replies%> repl<%if post_replies == 1%>y<%else%>ies<%endif%>,<%endif%> + authored by + <%set include_username_display = $post_username~%> + <%include include_username.html%> + on <%post_date%>. +

    + +

    + Detaching a post will turn that post and any replies into a new thread. + You also have the option now to place the new thread in another forum or + leave it in the same forum by selecting the forum below. +

    + +
    + <%hidden_form%> + + + <%GForum::Forum::moderator_tpl_options(without => $forum_id)%> +
    + + +
    +
    + +
    <%-- end content --%> +
    +
    + <%include include_footer.html%> +
    + +<%~include include_global_js.html%> + diff --git a/site/forum/post_detached.html b/site/forum/post_detached.html new file mode 100644 index 0000000..0580746 --- /dev/null +++ b/site/forum/post_detached.html @@ -0,0 +1,71 @@ + +<%~set search_widget = 1%> + + + <%cat_full_name%>: <%forum_name%>: Post detached<%if forum_id != $old_forum_id%> and moved<%endif%> + $old_forum_id, param_type => "query")%>" /> +<%include include_common_head.html%> + + +<%~if forum_id != $old_forum_id%><%set content_header_title .= ' and moved'%><%endif%> + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = 'Post Detached'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + <%~set breadcrumbs.1.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + +

    + The post ' $post_id)%>"><%post_subject%>', written by + <%set include_username_display = $post_username~%><%include include_username.html%> + has been detached from its thread and turned into a new, separate thread. +

    + <%~if forum_id != $old_forum_id%> +

    + The new thread was moved from the $old_cat_id)%>"><%old_cat_full_name%>: $old_forum_id)%>"><%old_forum_name%> forum + to the $cat_id)%>"><%cat_full_name%>: $forum_id)%>"><%forum_name%> forum. +

    + <%~endif%> +

    + You will be automatically redirected to <%old_cat_full_name%>: <%old_forum_name%> shortly. + <%~set url_anchor = ""%> + <%~unless current.user_id and not current.user_default_post_display%><%~set url_anchor = "#p$post_id"%><%endunless%> + $post_id, anchor => $url_anchor)%>">Click here to view the new thread + or choose the forum you wish to visit: +

    + +
    +
    + <%hidden_form%> + + +
    +
    + +
    <%-- end content --%> +
    +
    + <%include include_footer.html%> +
    + +<%~include include_global_js.html%> + diff --git a/site/forum/post_edit.html b/site/forum/post_edit.html new file mode 100644 index 0000000..0e4aadf --- /dev/null +++ b/site/forum/post_edit.html @@ -0,0 +1,4 @@ +<%~-- +This is the page used when you edit a post. +--%> +<%include post_write.html%> diff --git a/site/forum/post_edit_post.html b/site/forum/post_edit_post.html new file mode 100644 index 0000000..d4a03e4 --- /dev/null +++ b/site/forum/post_edit_post.html @@ -0,0 +1,75 @@ +<%~set url_anchor = ""%> +<%~unless current.user_id and not current.user_default_post_display%><%set url_anchor = "p$post_id"%><%endunless%> +<%~set search_widget = 1%> + + + + <%cat_full_name%>: Post changes made +<%~if current.user_do_after_post eq 'post_confirm_post'%> + $post_id, anchor => $url_anchor, param_type => "query")%>" /> +<%~else%> + $forum_id, param_type => "query")%>" /> +<%~endif%> + <%include include_common_head.html%> + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = 'Post changes made'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + <%~set breadcrumbs.1.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + + <%~if current.user_do_after_post eq post_confirm_post%> +

    + Your changes to the message in <%cat_full_name%>: <%forum_name%> have + been made. You will be automatically redirected to your post shortly. +

    +

    + $post_id, anchor => $url_anchor))%>">Click here to view your post + or choose the forum you wish to visit: +

    + <%~else%> +

    + Your changes to the message in <%cat_full_name%>: <%forum_name%> have + been made. You will be automatically redirected to the forum shortly. +

    +

    + $post_id, anchor => $url_anchor))%>">Click here to view your post + or choose the forum you wish to visit: +

    + <%~endif%> + +
    +
    + <%hidden_form%> + +   + +
    +
    + +
    <%-- end content --%> +
    <%-- end contentwrapper --%> +
    <%-- end main --%> + + <%include include_footer.html%> +
    + +<%~include include_global_js.html%> + diff --git a/site/forum/post_editlog.html b/site/forum/post_editlog.html new file mode 100644 index 0000000..8fe3b71 --- /dev/null +++ b/site/forum/post_editlog.html @@ -0,0 +1,56 @@ + +<%~set search_widget = 1%> + + + <%cat_full_name%>: Edit Log +<%include include_common_head.html%> + + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = 'Edit Log'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + <%~set breadcrumbs.1.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + +
    + <%set hide_post_menu = 1%> + <%include include_post_display.html%> +
    + +

    <%if editlog.length%>Edit Log:<%else%>This post has not been edited<%endif%>

    + + <%~if editlog.length%> +
      + <%~loop editlog%> +
    • + <%~set escaped_username = escape_url unescape_html $user_username%> + Post edited by <%if user_id%>"><%user_username%><%else%><%user_username%><%endif%> + (<%user_title%>) on <%edit_date%><%if edit_reason%>: <%edit_reason%><%endif%> +
    • + <%~endloop%> +
    + <%~endif%> + +
    <%-- end content --%> +
    +
    + <%include include_footer.html%> +
    + +<%~include include_global_js.html%> + diff --git a/site/forum/post_move_select.html b/site/forum/post_move_select.html new file mode 100644 index 0000000..8acbae8 --- /dev/null +++ b/site/forum/post_move_select.html @@ -0,0 +1,79 @@ +<%~GForum::Post::View::get($in.root_id)%> +<%~unless post_id%><%set error = GForum::language('POST_DOES_NOT_EXIST')%><%include error.html%><%endparse%><%endunless~%> +<%~set search_widget = 1%> + + + + <%cat_full_name%>: <%forum_name%>: <%if post_deleted%>Post deleted by <%post_deleted_by%>: <%endif%><%post_subject%>: Move Thread +<%include include_common_head.html%> + + + +
    + <%include include_header.html%> + +
    +
    + + <%~set content_header_title = 'Thread Move'%> + <%~set btn_new = 1%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + <%~set breadcrumbs.1.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> +

    + You have chosen to move the thread ' $post_id, anchor => "p$post_id")%>"><%post_subject%>', + <%if post_replies%>containing <%post_replies%> repl<%if post_replies == 1%>y<%else%>ies<%endif%>,<%endif%> + started by + <%set include_username_display = $post_username~%> + <%include include_username.html%> + on <%post_date%>. +

    +

    + Please select a forum to move the post to: +

    + +
    +
    + <%hidden_form%> + + + <%GForum::Forum::moderator_tpl_options(without => $forum_id)%> + + + + <%if forum_move_pointer%>
    + + <%endif%> +
    +
    + +
    <%-- end content --%> +
    <%-- end contentwrapper --%> +
    <%-- end main --%> + <%include include_footer.html%> +
    + +<%~include include_global_js.html%> + diff --git a/site/forum/post_moved.html b/site/forum/post_moved.html new file mode 100644 index 0000000..2d4fa0d --- /dev/null +++ b/site/forum/post_moved.html @@ -0,0 +1,72 @@ + +<%~set search_widget = 1%> + + + <%cat_full_name%>: <%forum_name%>: Thread moved + $old_forum_id, param_type => "query")%>" /> +<%include include_common_head.html%> + + + +
    + <%include include_header.html%> + +
    +
    + + <%~set content_header_title = 'Thread Moved'%> + <%~set btn_new = 1%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + <%~set breadcrumbs.1.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + +

    + The thread ' $post_id)%>"><%post_subject%>', written by + <%if user_id_fk%> + <%~if user_status%> + <%~set escaped_username = escape_url unescape_html $user_username%> + "><%nbsp post_username%> + <%~else%> + "><%nbsp post_username%> + <%~endif%> + <%else%> + <%nbsp post_username%> + <%endif%> + has been moved from the <%GForum::Category::full_name_linked($old_cat_id, ': ')%>: $old_forum_id)%>"><%old_forum_name%> forum + to the <%GForum::Category::full_name_linked($cat_id, ': ', '', '', "${config.gforum_cgi}?$hidden_url")%>: $forum_id)%>"><%forum_name%> forum. + You will be automatically redirected to <%old_cat_full_name%>: <%old_forum_name%> shortly. +

    +

    + <%~set url_anchor = ""%> + <%~unless current.user_id and not current.user_default_post_display%><%set url_anchor = "p$post_id"%><%endunless%> + $post_id, anchor => $url_anchor)%>">Click here to view the thread + or choose the forum you wish to visit: +

    + +
    +
    + <%hidden_form%> + +   + +
    +
    + +
    <%-- end content --%> +
    <%-- end contentwrapper --%> +
    <%-- end main --%> + <%include include_footer.html%> +
    + +<%~include include_global_js.html%> + diff --git a/site/forum/post_post.html b/site/forum/post_post.html new file mode 100644 index 0000000..f042f7d --- /dev/null +++ b/site/forum/post_post.html @@ -0,0 +1,83 @@ +<%~set url_anchor = ""%> +<%~unless current.user_id and not current.user_default_post_display%><%~set url_anchor = "p$post_id"%><%endunless%> +<%~set search_widget = 1%> + + + + <%cat_full_name%>: Message Posted +<%~if current.user_do_after_post eq post_confirm_post%> + $post_id, anchor => $url_anchor, param_type => "query")%>" /> +<%~else%> + $forum_id, param_type => "query")%>" /> +<%~endif%> +<%include include_common_head.html%> + + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = 'Your new message has been posted'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + <%~set breadcrumbs.0.name = "Post new topic"%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + + <%~if attachment_errors%> +

    + <%attachment_errors%> +

    + <%~endif%> + + <%~if current.user_do_after_post eq post_confirm_post%> +

    + Your message has been posted to <%cat_full_name%>: <%forum_name%>. You + will be automatically redirected to your post shortly. +

    +

    + $post_id, anchor => $url_anchor))%>">Click here to view your post + or choose the forum you wish to visit: +

    + <%~else%> +

    + Your message has been posted to <%cat_full_name%>: <%forum_name%>. You + will be automatically redirected to the forum shortly. +

    +

    + $post_id, anchor => $url_anchor))%>">Click here to view your post + or choose the forum you wish to visit: +

    + <%~endif%> + +
    +
    + <%hidden_form%> + +   + +
    +
    + +
    <%-- end content --%> +
    +
    + + <%include include_footer.html%> + +
    + +<%~include include_global_js.html%> + diff --git a/site/forum/post_reply_post.html b/site/forum/post_reply_post.html new file mode 100644 index 0000000..41e5d08 --- /dev/null +++ b/site/forum/post_reply_post.html @@ -0,0 +1,79 @@ + + + + <%cat_full_name%>: <%forum_name%>: Message posted! +<%~if current.user_do_after_post eq 'post_confirm_post'%> + $post_id, anchor => $url_anchor, param_type => "query")%>" /> +<%~else%> + $forum_id, param_type => "query")%>" /> +<%~endif%> +<%include include_common_head.html%> + + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = $content_header_title%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + <%~set breadcrumbs.1.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + + <%~if attachment_errors%> +

    <%attachment_errors%>

    + <%~endif%> + + <%~if current.user_do_after_post eq post_confirm_post%> +

    + Your message has been posted to <%cat_full_name%>: <%forum_name%>. You + will be automatically redirected to your post shortly. +

    +

    + $post_id, anchor => $url_anchor))%>">Click here to view your post + or choose the forum you wish to visit: +

    + <%~else%> +

    + Your message has been posted to <%cat_full_name%>: <%forum_name%>. You will be automatically redirected to the forum shortly. +

    +

    + $post_id, anchor => $url_anchor))%>">Click here to view your post + or choose the forum you wish to visit: +

    + <%~endif%> + +
    + <%hidden_form%> +
    + +   + +
    +
    +
    <%-- end content --%> + +
    + +
    +
    + + <%include include_footer.html%> + +
    + +<%~include include_global_js.html%> + diff --git a/site/forum/post_reply_write.html b/site/forum/post_reply_write.html new file mode 100644 index 0000000..528d0f0 --- /dev/null +++ b/site/forum/post_reply_write.html @@ -0,0 +1,4 @@ +<%~-- +This is the page used when you write a post reply. +--%> +<%include post_write.html%> diff --git a/site/forum/post_report_box_get.html b/site/forum/post_report_box_get.html new file mode 100644 index 0000000..6e310b1 --- /dev/null +++ b/site/forum/post_report_box_get.html @@ -0,0 +1,26 @@ + +
    +

    Report this Post

    +
    +
    +
    +
    +
    + +
    <%config.post_reports_message_max_length%> Characters left.
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    diff --git a/site/forum/post_reports_box_get.html b/site/forum/post_reports_box_get.html new file mode 100644 index 0000000..420dbbb --- /dev/null +++ b/site/forum/post_reports_box_get.html @@ -0,0 +1,46 @@ + +
    + + <%~if post.post_reports_is_locked%> + Locked till <%post.post_reports_locked%> + <%~else%> + Lock for moderation + <%~endif%> + Mark Processed + Send PMs + + Reported Post + - View Report Comments + - Send PMs +
    +
    +
    + + <%loop post_reports%> + + + <%~set escaped_username = escape_url unescape_html $user_username%> + + + + <%endloop%> +
    "><%user_username%>:<%message%>
    +
    +
    + + + + + + +
    + Send PMs to all reporters + Send PM to author (<%post.post_username%>) +
    +
    +
    +
    diff --git a/site/forum/post_view_flat.html b/site/forum/post_view_flat.html new file mode 100644 index 0000000..f327a6f --- /dev/null +++ b/site/forum/post_view_flat.html @@ -0,0 +1,139 @@ + +<%~set search_widget = 1%> +<%~set poll_widget = 1%> + + + <%root_post_subject%><%if this_page > 1%> (Page <%this_page%>)<%endif%>: <%forum_name%>: <%cat_full_name%> + <%include include_common_head.html%> + <%~if this_do eq post_view_printable%> + + <%~endif%> + + + +<%~set hidden_sort = ''%><%set hidden_page = ''%> +<%~if in.sb%><%set hidden_sort .= ";sb=$sb"%><%endif%> +<%~if in.so%><%set hidden_sort .= ";so=$so"%><%endif%> +<%~if in.mh%><%set hidden_page .= ";mh=$mh"%><%endif%> +<%~if in.page%><%set hidden_page .= ";page=$this_page"%><%endif%> + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = $forum_name%> + <%~set post_toolbar = 1%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + <%~set breadcrumbs.1.name = $root_post_subject%> + + <%include include_breadcrumb.html%> +
    <%-- start content--%> + <%include include_content_header.html%> + +<%~set root_post_id ||= $post_root_id || $post_id%> +<%~set do = ''%> +<%~if in.do%> + <%~set do = "do=$this_do"%> +<%~endif%> +<%~set pagination_options.type = 'post'%> +<%~set pagination_options.id = $root_post_id%> +<%~set pagination_options.params = "$do$hidden_sort$hidden_page"%> +<%~set pagination_options.page_items = $num_posts%> +<%~set pagination_options.show_view_all = 1%> + +
    + <%include include_paging.html%> + <%include include_quicklaunch.html%> +
    + + <%loop post_loop%> + <%~if is_moderator and post_reports_count%><%set admin_show_post = 1%><%else%><%set admin_show_post = 0%><%endif%> + <%~if blocked and not admin_show_post and in.post != $post_id%><%set hide_post = 1%><%else%><%set hide_post = 0%><%endif%> + + <%-- Serve ad on mobile --%> + <%~if row_num == 1%> + <%~set serve_ad = 1%> + <%~else%> + <%~set count += 1%><%set odd = $count % 30%> + <%-- disabled additional ads - ticket #31747 --%> + <%~if not $odd%><%set serve_ad = 0%><%endif%> + <%~endif%> + + <%include include_post_display.html%> + + <%endloop%> + + <%include include_post_toolbar.html%> + + <%-- ad for tablet --%> + <%~if num_posts > 3%> + <%include include_ad_tablet.html%> + <%~endif%> + +
    <%-- end content --%> +
    <%-- end contentwrapper --%> +
    <%-- end main --%> + + <%include include_footer.html%> +
    + +<%if spellcheck%><%spellcheck_head%><%endif%> +<%include include_global_js.html%> + + diff --git a/site/forum/post_view_printable.html b/site/forum/post_view_printable.html new file mode 100644 index 0000000..e470fb3 --- /dev/null +++ b/site/forum/post_view_printable.html @@ -0,0 +1 @@ +<%include post_view_flat.html%> diff --git a/site/forum/post_view_threaded.html b/site/forum/post_view_threaded.html new file mode 100644 index 0000000..e470fb3 --- /dev/null +++ b/site/forum/post_view_threaded.html @@ -0,0 +1 @@ +<%include post_view_flat.html%> diff --git a/site/forum/post_write.html b/site/forum/post_write.html new file mode 100644 index 0000000..cb457b3 --- /dev/null +++ b/site/forum/post_write.html @@ -0,0 +1,100 @@ + + + + <%site_title%>: <%cat_full_name%>: <%forum_name%>: <%if this_do eq post_reply_write%>Reply to post<%elsif this_do eq post_edit%>Edit post<%else%>Post a new message<%endif%> +<%include include_common_head.html%> + + +<%~if this_do eq post_reply_write%> + <%~set content_header_title = 'Reply to post'%> + <%~set content_header_subtitle = "Replying to ${parent.post_subject}"%> +<%~elsif this_do eq post_edit%> + <%~set content_header_title = 'Edit post'%> + <%~set content_header_subtitle = "Editing ${orig.post_subject}"%> +<%~else%> + <%~set content_header_title = 'Post a New Message'%> +<%~endif%> + + + +
    + <%include include_header.html%> + +
    +
    + + + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + <%~set breadcrumbs.1.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + +
    + + <%preview || ''%> + + <%~set url_anchor = ""%> + <%~if current.user_do_after_post eq post_view%> + <%~set url_anchor = "last"%> + <%~if $post_id%><%set url_anchor = "p$post_id"%><%endif%> + <%~endif%> +
    + <%hidden_form%> + <%~if this_do eq post_reply_write%> + + <%~elsif this_do eq post_edit%> + + <%~else%> + + <%~endif%> + + <%include include_post_write.html%> + +
    + <%~if this_do eq post_reply_write%> + + <%~elsif this_do eq post_edit%> + + <%~else%> + + <%~endif%> +
    +
    + +
    + <%~if this_do eq post_reply_write%> + <%~parent%><%-- Loads the parents post tags --%> + <%~set preview = 0%><%set reply_view = 1%><%set hide_post_menu = 1%> + <%include include_post_display.html%> + <%~elsif this_do eq post_edit%> + <%~orig%><%-- Loads the original post tags --%> + <%~set preview = 0%><%set orig_view = 1%><%set hide_post_menu = 1%> + <%include include_post_display.html%> + <%~endif%> + +
    <%-- end content --%> + +
    + +
    +
    + + <%include include_footer.html%> +
    + +<%~include include_global_js.html%> + +<%include include_global_js.html%> + + diff --git a/site/forum/redirect.html b/site/forum/redirect.html new file mode 100644 index 0000000..d04b930 --- /dev/null +++ b/site/forum/redirect.html @@ -0,0 +1,42 @@ + + +<:: Slowtwitch Forums ::> + +<%include include_common_head.html%> + + +<%~set content_header_title = 'Redirect'%> +<%~set content_header_subtitle = 'You are being redirected'%> + + + <%~if ad_wallpaper%> + <%ad_wallpaper%> + <%~endif%> + +
    + <%include include_header.html%> +
    +
    + + + <%~set content_header_title = 'Forum'%> + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + +

    + You are being redirected to the selected URL. If you are not + automatically redirected, click here: <%url%> +

    + +
    <%-- end content --%> +
    +
    + <%include include_footer.html%> +
    + +<%include include_global_js.html%> + diff --git a/site/forum/resend_validation_enter_username.html b/site/forum/resend_validation_enter_username.html new file mode 100644 index 0000000..7c68d1b --- /dev/null +++ b/site/forum/resend_validation_enter_username.html @@ -0,0 +1,62 @@ + + + + <%cat_full_name%>: Resend E-mail Validation +<%include include_common_head.html%> + + +<%~set content_header_title = 'Resend E-mail Validation'%> + + +
    + <%include include_header.html%> + +
    +
    + + + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + +
    + <%hidden_form%> + +

    + If you recently signed up for an account, but never received the validation e-mail, + <%site_title%> can resend the validation e-mail to you. +

    + +
    +
    +
    Your Username:
    +
    +
    + +
    +
    +
    +
    +
    +
    + +

    * The e-mail will be re-sent to the e-mail address you entered when signing up.

    +
    +
    +
    +
    + +
    <%-- end content --%> +
    +
    + <%include include_footer.html%> +
    + +<%~include include_global_js.html%> + diff --git a/site/forum/search.html b/site/forum/search.html new file mode 100644 index 0000000..be23618 --- /dev/null +++ b/site/forum/search.html @@ -0,0 +1,223 @@ + + + + <%cat_full_name%>: Search + <%include include_common_head.html%> + + +
    + <%include include_header.html%> + +
    + <%~set content_header_title = 'Search'%> + + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = 'Search'%> + +
    + + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + +
    +
    +

    Search Forums

    +
    + <%hidden_form%> + +
    +
    +
    Search
    +
    +
    + +
    +
    +
    +
    Search String
    +
    +
    + value="<%search_string%>"<%endif%> class="txt midtext" /> +
    +
    +
    +
    Type of search*
    +
    +
    + +
    +
    +
    +
    Fields to search
    +
    +
    + +
    +
    +
    +
    Show posts from the last
    +
    +
    + +
    +
    +
    +
    Only show posts made by
    +
    +
    + value="<%search_user_username%>"<%endif%> class="txt shotext" /> +
    +
    +
    +
    Only show
    +
    +
    + +
    +
    +
    +
    Sort posts by
    +
    +
    + +
    +
    +
    +
    Number of results per page
    +
    +
    + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    *The search function breaks on numbers. So searching, for example, for "P5X" gives the same results as searching for "P"+"5"+"X." If you want to search for the phrase that contains a numeral like, "P5X," you need to select "Whole Phrase" from the "Type of Search" drop-down.
    +
    +
    +

    Search SlowTwitch.com Knowledge Base

    +
    + <%hidden_form%> +
    +
    +
    Search string
    +
    +
    + +
    +
    +
    +
    Type of search
    +
    +
    + +
    +
    +
    +
    Only Show
    +
    +
    + +
    +
    +
    +
    Link per page
    +
    +
    + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + +
    <%-- end content --%> +
    +
    + <%include include_footer.html%> +
    + +<%include include_global_js.html%> + diff --git a/site/forum/search_results.html b/site/forum/search_results.html new file mode 100644 index 0000000..86a53a7 --- /dev/null +++ b/site/forum/search_results.html @@ -0,0 +1,92 @@ + +<%~set search_widget = 1%> +<%~set poll_widget = 1%> + + +<%cat_full_name%>: Search results +<%include include_common_head.html%> + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = "Search Results"%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + +

    Your search <%if search_words or limited_by_user%>for <%endif%><%if search_words%><%search_words%> <%if limited_by_user%> in<%endif%><%endif%><%if limited_by_user%> posts made by <%limited_by_user%><%endif%> returned <%num_results%> results

    +

    [N.B. The search function breaks on numbers. So searching, for example, for "P5X" gives the same results as searching for "P"+"5"+"X." If you want to search for the phrase that contains a numeral like, "P5X," you need to select "Whole Phrase" from the "Type of Search" drop-down.]

    +
    + +
    + <%hidden_form%> + +
    + + <%~set post_list = $results%> + <%~set post_list_table_class = 'search-list'%> + <%~if search_string ne ''%> + <%~set post_list_query_string = 'search_string='%> + <%~set post_list_query_string .= escape_url unescape_html $search_string%> + <%~endif%> + <%~set post_list_show_replies = 1%> + <%~include include_post_list.html%> + + <%~if num_results > $mh%> +
    +
    + + <%~set pagination_options.params = "do=$this_do;$search_query;sb=$sb;mh=$mh"%> + <%~set pagination_options.page_items = $num_results%> + <%include include_paging.html%> +
    + <%~endif%> + + +
    <%-- end content --%> +
    <%-- end contentwrapper --%> +
    <%-- end main --%> + + <%include include_footer.html%> +
    + +<%~include include_global_js.html%> + diff --git a/site/forum/spellcheck_inline.html b/site/forum/spellcheck_inline.html new file mode 100644 index 0000000..1c22119 --- /dev/null +++ b/site/forum/spellcheck_inline.html @@ -0,0 +1,58 @@ + + + + <%site_title%> + + + +
    + <%hidden_form%> + + + + + + + + +
    + + diff --git a/site/forum/threadtag_form.html b/site/forum/threadtag_form.html new file mode 100644 index 0000000..40ad21b --- /dev/null +++ b/site/forum/threadtag_form.html @@ -0,0 +1,209 @@ + + + + <%cat_full_name%>: Tag a Thread + <%include include_common_head.html%> + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = 'Tag a Thread'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + <%~set breadcrumbs.1.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> +
    + + + +
    +
    +
    Thread
    +
    +
    + <%post_subject%> - + $post_id)%>" class="title">view thread +
    +
    +
    +
    Categories
    +
    +
    + <%~loop groups_loop%> +

    <%group_name%> - edit - add subcategory

    +
      + <%~loop children%> +
    • checked<%endif%>> - edit
    • + <%~endloop%> +
    + <%~endloop%> +

    add category

    +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    <%-- end content --%> +
    <%-- end contentwrapper --%> +
    <%-- end main --%> + + <%include include_footer.html%> +
    +
    +
    + +
    +
    +
    + +
    +
    +
    +
    Category
    +
    + +
    +
    +
    +
    Name
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    + +<%~include include_global_js.html%> + + + diff --git a/site/forum/user_favourites.html b/site/forum/user_favourites.html new file mode 100644 index 0000000..e04630e --- /dev/null +++ b/site/forum/user_favourites.html @@ -0,0 +1,39 @@ + + + + <%cat_full_name%>: Favourite Posts +<%include include_common_head.html%> + + + +
    + <%include include_header.html%> + +
    + <%~set content_header_title = "Favourite Posts"%> + + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $user_username%> + <%~set breadcrumbs.0.url = GForum::SEO::url(type => 'profile', id => $user_username)%> + <%~set breadcrumbs.1.name = $content_header_title%> + +
    + + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + + <%include include_user_favourites.html%> +
    <%-- end content --%> +
    + +
    + <%include include_footer.html%> +
    + +<%~include include_global_js.html%> + diff --git a/site/forum/user_ips.html b/site/forum/user_ips.html new file mode 100644 index 0000000..cba84ae --- /dev/null +++ b/site/forum/user_ips.html @@ -0,0 +1,104 @@ + + + + <%cat_full_name%>: User IP summary: <%user_username%> +<%include include_common_head.html%> + + +<%~set content_header_title = $user_username%> +<%~set content_header_subtitle = 'User IP summary'%> + + +
    + <%include include_header.html%> + +
    +
    + + + <%set content_header_title = "User IP Summary $user_username"%> + <%set content_header_title .= $ip%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + + <%~if not ips.length%> +

    + The user <%include include_username.html%> has not made any posts yet. +

    + <%~else%> +

    + The user <%include include_username.html%> has posted to <%cat_full_name%> from the following IP's: +

    + + <%~unless current.user_status == $ADMINISTRATOR%> +

    Note that information is only shown for forums which you moderate.

    + <%~endunless%> + +
    +
    +
    +
    IP Address
    +
    Posts
    +
    Latest Post
    +
    Other Posters
    +
    +
    +
    + <%~loop ips%> +
    +
    + <%ip%> + <%~if country_lookups%> + (<%if ip_country%><%ip_country%><%else%>Unknown<%endif%>) + <%~endif%> +
    + +
    <%GForum::date($post_time)%>
    +
    + <%~loop others~%> + <%~%> + <%include include_username.html%> + (<%~%> + <%~set url_user_username = escape_url unescape_html $user_username%> + IPs + <%~if current.user_status == $ADMINISTRATOR and user_status != $ADMINISTRATOR%> + | + <%~if user_enabled%><%set url_user_enabled = disable_user%><%else%><%set url_user_enabled = enable_user%><%endif%> + <%~set url_search_username = escape_url unescape_html $search_user.user_username%> + title="User account is disabled"<%endif%> rel="post-only"><%if user_enabled%>Dis<%else%>En<%endif%>able User + <%~endif~%> + )<%~%> + + <%~unless last%>, <%endunless%> + <%~endloop%> +
    +
    + <%~endloop%> +
    +
    + <%~endif%> + +
    <%-- end content --%> +
    +
    + <%include include_footer.html%> +
    + +<%~include include_global_js.html%> +<%~if current.user_status == $ADMINISTRATOR%> + +<%~endif%> + diff --git a/site/forum/user_list.html b/site/forum/user_list.html new file mode 100644 index 0000000..fa6e12f --- /dev/null +++ b/site/forum/user_list.html @@ -0,0 +1,232 @@ + + + + <%cat_full_name%>: Member Directory<%if this_do eq 'user_search'%> Search Results<%endif%> + <%include include_common_head.html%> + +<%~if this_do eq user_search%> +<%~set url_header = GForum::SEO::url(params => "do=user_list")%> +<%~set content_header_breadcrumb = "Member Directory"%> +<%~set content_header_title = 'Search Results'%> +<%~else%> +<%~set content_header_title = 'Member Directory'%> +<%~endif%> + +<%~set hidden_sort = ''%><%set hidden_page = ''%> +<%~if in.sb%><%set hidden_sort .= ";sb=$sb"%><%endif%> +<%~if in.so%><%set hidden_sort .= ";so=$so"%><%endif%> +<%~if in.mh%><%set hidden_page .= ";mh=$mh"%><%endif%> +<%~set mh = $in.mh%> +<%~set mh ||= $current.user_default_mh_user%> +<%~set mh ||= $config.default_mh_user%> + + +
    + <%include include_header.html%> + +
    +
    + + + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + +

    + <%~if this_do eq user_search%> + Your search returned <%num_page_items%> result<%if num_page_items != 1%>s<%endif%>. + <%~elsif this_do eq user_list and first%> + <%num_page_items%> user<%if num_page_items != 1%>s<%endif%> found starting with <%if first eq NUM%>0 - 9<%elsif first eq OTHER%>Other<%else%>'<%first%>'<%endif%>. + <%~else%> + There are <%registered_users%> registered users. + <%~endif%> +

    + + + + <%~if this_do eq user_list%> + <%~set base_url = GForum::SEO::url(params => "do=user_list;sb=user_username;so=")%> + <%~if so%> + <%~set base_url .= $so%> + <%~else%> + <%~set base_url .= 'ASC'%> + <%~endif%> +
    +
    + <%if first eq NUM%>0-9<%elsif has_num%>0-9<%else%>0-9<%endif%> + <%if first eq A%>A<%elsif has_a%>A<%else%>A<%endif%> + <%if first eq B%>B<%elsif has_b%>B<%else%>B<%endif%> + <%if first eq C%>C<%elsif has_c%>C<%else%>C<%endif%> + <%if first eq D%>D<%elsif has_d%>D<%else%>D<%endif%> + <%if first eq E%>E<%elsif has_e%>E<%else%>E<%endif%> + <%if first eq F%>F<%elsif has_f%>F<%else%>F<%endif%> + <%if first eq G%>G<%elsif has_g%>G<%else%>G<%endif%> + <%if first eq H%>H<%elsif has_h%>H<%else%>H<%endif%> + <%if first eq I%>I<%elsif has_i%>I<%else%>I<%endif%> + <%if first eq J%>J<%elsif has_j%>J<%else%>J<%endif%> + <%if first eq K%>K<%elsif has_k%>K<%else%>K<%endif%> + <%if first eq L%>L<%elsif has_l%>L<%else%>L<%endif%> + <%if first eq M%>M<%elsif has_m%>M<%else%>M<%endif%> + <%if first eq N%>N<%elsif has_n%>N<%else%>N<%endif%> + <%if first eq O%>O<%elsif has_o%>O<%else%>O<%endif%> + <%if first eq P%>P<%elsif has_p%>P<%else%>P<%endif%> + <%if first eq Q%>Q<%elsif has_q%>Q<%else%>Q<%endif%> + <%if first eq R%>R<%elsif has_r%>R<%else%>R<%endif%> + <%if first eq S%>S<%elsif has_s%>S<%else%>S<%endif%> + <%if first eq T%>T<%elsif has_t%>T<%else%>T<%endif%> + <%if first eq U%>U<%elsif has_u%>U<%else%>U<%endif%> + <%if first eq V%>V<%elsif has_v%>V<%else%>V<%endif%> + <%if first eq W%>W<%elsif has_w%>W<%else%>W<%endif%> + <%if first eq X%>X<%elsif has_x%>X<%else%>X<%endif%> + <%if first eq Y%>Y<%elsif has_y%>Y<%else%>Y<%endif%> + <%if first eq Z%>Z<%elsif has_z%>Z<%else%>Z<%endif%> + <%if first eq OTHER%>Other<%elsif has_other%>Other<%endif%> + <%if not first%>All<%else%>All<%endif%> +
    +
    + <%endif%> + +
    +
    +
    +
    + <%~if sb eq user_username and so ieq asc%><%set url_so = 'desc'%><%else%><%set url_so = 'asc'%><%endif%> + <%~if first%><%set first = escape_url first%><%set first = ";first=$first"%><%else%><%set first = ""%><%endif%> + + Username + <%~if sb eq user_username%> <%endif~%> + +
    +
    Online
    +
    Title
    +
    + <%~if sb eq user_posts and so ieq asc%><%set url_so = 'desc'%><%else%><%set url_so = 'asc'%><%endif%> + <%~if first%><%set first = escape_url first%><%set first = ";first=$first"%><%else%><%set first = ""%><%endif%> + <%~%> + Posts + <%~if sb eq user_posts%> <%endif~%> + +
    +
    + <%~if sb eq user_last_seen and so ieq asc%><%set url_so = 'desc'%><%else%><%set url_so = 'asc'%><%endif%> + <%~if first%><%set first = escape_url first%><%set first = ";first=$first"%><%else%><%set first = ""%><%endif%> + <%~%> + Last seen + <%~if sb eq user_last_seen%> <%endif~%> + +
    +
    +
    +
    + <%~loop users%> +
    +
    + <%~if current.user_status == $ADMINISTRATOR%> + <%include include_username.html%> + <%if user_status == $ADMINISTRATOR%><%-- Admin users cannot disable/enable other admin users --~%> + — Disable + <%elsif user_enabled~%> + — Disable + <%else~%> + — Enable + <%endif%> + <%~else%> + <%include include_username.html%> + <%~endif%> + — ">Message +
    +
    + <%~if user_invisible and current.user_status < $ADMINISTRATOR%>No + <%~elsif user_online%>Yes + <%~else%>No + <%~endif~%> +
    +
    <%user_title%>
    + <%~set url_user_username = escape_url unescape_html $user_username%> +
    <%if user_posts%><%user_posts%><%else%>0<%endif%>
    +
    + <%~if user_invisible and current.user_status < $ADMINISTRATOR~%> + + <%~else~%> + <%if user_invisible%><%endif~%> + <%~endif~%> +
    +
    + <%~endloop%> +
    +
    + + <%if num_page_items > $mh%> +
    +
    +
    + <%include include_paging.html%> +
    +
    + <%~endif%> + +
    <%-- end content --%> +
    +
    + <%include include_footer.html%> +
    + + +<%~include include_global_js.html%> +<%~if current.user_status == $ADMINISTRATOR%> + +<%~endif%> + diff --git a/site/forum/user_profile.html b/site/forum/user_profile.html new file mode 100644 index 0000000..8e20038 --- /dev/null +++ b/site/forum/user_profile.html @@ -0,0 +1,99 @@ + + + + <%cat_full_name%>: Profile + <%include include_common_head.html%> + + +
    + <%include include_header.html%> + +
    + <%~set content_header_title = 'Profile Settings'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = 'Profile Settings'%> + <%~if changes_saved%><%set success = 'The changes to your profile have been saved successfully.'%><%endif%> + +
    + + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + +
    +
    +
    +
    + +

    View My Profile

    +

    View your user profile here. Make changes to your saved bike fit coordinates, update your personal race calendar, edit your profile photo album, and see how your profile looks to others.

    +
    +
    +
    +
    +
    + +

    Profile Settings

    +

    From here you can change your username, password, email addresses, user icon, signature, and cookie settings.

    +
    +
    +
    +
    +
    +
    +
    + +

    Display Settings

    +

    Change all your default display settings here, including text area sizes and post styles.

    +
    +
    +
    +
    +
    + +

    Subscriptions

    +

    If you subscribe to any of the forums listed here, you will receive daily email notifications of all the posts made in the last day.

    +
    +
    +
    +
    +
    +
    +
    + +

    Watched Threads

    +

    If you have watched any threads, and no longer wish to receive updates when threads are updated, come to this page to stop watching any or all of your watched threads.

    +
    +
    +
    +
    +
    + +
    <%-- end content --%> +
    +
    + + <%include include_footer.html%> +
    + +<%~include include_global_js.html%> + diff --git a/site/forum/user_profile_basic.html b/site/forum/user_profile_basic.html new file mode 100644 index 0000000..605e6de --- /dev/null +++ b/site/forum/user_profile_basic.html @@ -0,0 +1,357 @@ +<%Plugins::GForum::SlideShow::generate_paths($user_id)%> + + + + <%site_title%>: Settings: Profile: <%current.user_username%> + <%include include_common_head.html%> + <%--GForum::User::icons(120, 100)--%><%-- If you change the icon size restrictions, be sure to also change the css --%> + + +
    + <%include include_header.html%> + +
    + <%~set content_header_title = 'Edit Profile'%> + + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = 'User Profile'%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "do=user_profile")%> + <%~set breadcrumbs.1.name = $current_user_username%> + +
    + + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> +
    + + + + <%hidden_form%> + +
    +
    +
    Username
    +
    +
    + +
    +
    +
    +
    E-mail
    +
    +
    + +
    +
    +
    +
    Display E-mail
    +
    +
    + +
    +
    +
    +
    Change Password
    +
    +
    + +
    +
    +
    +
    Change Password (again)
    +
    +
    + +
    +
    +
    +
    + Signature
    + <%~if config.signature_allow_html%>html ok<%endif%> + <%~if config.signature_allow_markup%> + (markup ok + <%~if config.signature_allow_markup == 1%> + [image] tags disabled + <%~endif%>) + <%~else%> + markup disabled + <%~endif%> +
    +
    +
    + +
    +
    +
    +
    Occupation
    +
    +
    + +
    +
    +
    +
    Allow Private Messages
    +
    +
    + +
    +
    +
    +
    Send private message notification via e-mail
    +
    +
    + +
    +
    +
    +
    Send reply notification selected="selected" by default
    +
    +
    + +
    +
    +
    +
    Disable reply notification
    +
    +
    + +
    +
    +
    +
    Allow others to see your selected races & retailers
    +
    +
    + +
    +
    +
    +
    Images
    +
    +
    +
    +
    + + <%~if user_image1_thumbnail_path%> + + <%~endif%> + + + + <%~if user_image1_thumbnail_path%> + Delete it + <%endif%> + +
    +
    + + <%~if user_image2_thumbnail_path%> + + <%~endif%> + + + + <%~if user_image2_thumbnail_path%> + Delete it + <%endif%> + +
    +
    + + <%~if user_image3_thumbnail_path%> + + <%~endif%> + + + + <%~if user_image3_thumbnail_path%> + Delete it + <%endif%> + +
    +
    +
    +
    + + <%~if user_image4_thumbnail_path%> + + <%~endif%> + + + + <%~if user_image4_thumbnail_path%> + Delete it + <%endif%> + +
    +
    + + <%~if user_image5_thumbnail_path%> + + <%~endif%> + + + + <%~if user_image5_thumbnail_path%> + Delete it + <%endif%> + +
    +
    + + <%~if user_image6_thumbnail_path%> + + <%~endif%> + + + + <%~if user_image6_thumbnail_path%> + Delete it + <%endif%> + +
    +
    +
    +
    +
    +
    Real Name
    +
    +
    + +
    +
    +
    +
    Homepage
    +
    +
    + +
    +
    +
    +
    Location
    +
    +
    + +
    +
    +
    +
    Interests
    +
    +
    + +
    +
    +
    +
    Social Handles
    +
    + +
    +
    +
    Country
    +
    +
    + <%~set countries = Plugins::GForum::ForumTrainingRegister::country_codes%> + +
    +
    +
    +
    Sex
    +
    +
    + +
    +
    +
    +
    Units of Measure
    +
    +
    + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    <%-- end content --%> +
    + +
    +<%include include_footer.html%> +
    + <%include markup_help.html%> +
    + +<%~include include_global_js.html%> + + diff --git a/site/forum/user_profile_display.html b/site/forum/user_profile_display.html new file mode 100644 index 0000000..338484c --- /dev/null +++ b/site/forum/user_profile_display.html @@ -0,0 +1,267 @@ + + + + <%site_title%>: Settings: Display Settings: <%current.user_username%> + <%include include_common_head.html%> + + + +
    + <%include include_header.html%> + +
    + <%~set content_header_title = 'Display Settings'%> + + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = 'User Profile'%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "do=user_profile")%> + <%~set breadcrumbs.1.name = 'Display Settings'%> + +
    + + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> +
    + + + + + <%hidden_form%> + +
    +
    +
    Post Style
    +
    +
    + +
    +
    +
    +
    Private Message Style
    +
    +
    + +
    +
    +
    +
    View Mode
    +
    +
    + +
    +
    +
    +
    Post Editor
    +
    +
    + +
    +
    +
    +
    After Posting
    +
    +
    + +
    +
    +
    +
    Posts per Page
    +
    +
    + +
    +
    +
    +
    PMs per Page
    +
    +
    + +
    +
    +
    +
    Search Results per Page
    +
    +
    + +
    +
    +
    +
    Search Results only Show
    +
    +
    + +
    +
    +
    +
    Users per Page
    +
    +
    + +
    +
    +
    +
    Current time:
    <%GForum::date%>
    +
    +
    + <%set default_tz = $config.default_timezone~%> + <%set init_zone = 0~%> + <%if default_tz eq 'local'%><%set default_tz = GT::Date::tz_local_name()%><%endif%> + <%if default_tz starts '+' or default_tz starts '-'%><%set disp_tz = 'UTC'%><%set disp_tz .= substr($default_tz, 0, 3)%><%set disp_tz .= ':'%><%set disp_tz .= substr($default_tz, 3)%><%set default_tz = $disp_tz%><%endif%> + + + + + + <%~loop datetime_timezones%> + <%~loop locations%> + <%~if user_timezone eq "$region/$loop_value"%><%set init_zone = $region%><%if init_zone eq 'America'%><%set init_zone = 'Americas'%><%endif%><%endif%> + <%~endloop%> + <%~endloop%> + + + + +
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    <%-- end content --%> +
    + + <%include include_footer.html%> +
    + +<%~include include_global_js.html%> + diff --git a/site/forum/user_profile_notifications.html b/site/forum/user_profile_notifications.html new file mode 100644 index 0000000..31b7776 --- /dev/null +++ b/site/forum/user_profile_notifications.html @@ -0,0 +1,131 @@ + + + + <%site_title%>: Settings: Display Settings: <%current.user_username%> + <%include include_common_head.html%> + + +
    + <%include include_header.html%> + +
    + <%~set action = $in.action || 'email'%> + <%~if $action eq 'email'%> + <%~set content_header_title = 'Email Subscriptions'%> + <%~elsif $action eq 'threads'%> + <%~set content_header_title = 'Watched Threads'%> + <%~endif%> + + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = 'User Profile'%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "do=user_profile")%> + <%~set breadcrumbs.1.name = $content_header_title%> + +
    + + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + +
    + + + + <%hidden_form%> + + <%~if action eq 'email'%> +

    You can subscribe/unsubscribe to the following forums:

    +
    +
    +
    +
    + +
    +
    Forum Name
    +
    +
    +
    + <%~loop all_forums%> + <%~if forum_id%> +
    +
    + <%~if forum_permission >= 2 %> checked="checked"<%endif%> /><%endif%> +
    +
    <%forum_name%>
    +
    + <%~endif%> + <%~endloop%> +
    +
    + +
    +
    +
    + +
    +
    + + <%~elsif $action eq 'threads'%> + +

    From this page you can unsubscribe from watched threads. To start watching + a thread, click the "Watch Thread" button shown when viewing a thread. When + a thread is watched, you will receive e-mail notifications when someone posts + a reply to the thread.

    +

    You are currently watching <%num_threads || 0%> threads:

    + + <%~if threads.length%> + <%GForum::Utils::new_alternation(threads)%> +
    +
    +
    +
    + +
    +
    Subject
    +
    +
    <%-- end dhead --%> +
    + <%~loop threads%> + + <%~endloop%> +
    +
    <%-- end dtable --%> +
    +
    +
    + +
    +
    + <%~endif%> + + <%~endif%> + +
    + +
    +
    <%-- end content --%> +
    + +
    +<%include include_footer.html%> + +<%~include include_global_js.html%> + + diff --git a/site/forum/user_signup.html b/site/forum/user_signup.html new file mode 100644 index 0000000..645c215 --- /dev/null +++ b/site/forum/user_signup.html @@ -0,0 +1,156 @@ + + + + <%cat_full_name%>: Sign Up +<%include include_common_head.html%> + + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = 'Sign Up'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + +

    You can sign up to the forum by entering your information here.

    + <%GForum::Config::tpl_get(require_signup_email_validation)%> + <%if require_signup_email_validation%> +

    If you have already filled out the signup form but still have not received a validation e-mail, click here to have the validation e-mail re-sent.

    + <%endif%> + +
    + + <%hidden_form%> + +
    +
    +
    Username *
    +
    +
    + +
    +
    +
    +
    Password *
    +
    +
    + +
    Password Strength: None
    +
    +
    +
    +
    Confirm Password *
    +
    +
    + +
    +
    +
    +
    E-mail Address *
    +
    +
    + +
    Used for admin purposes only. We will never make this public.
    +
    +
    +
    +
    Country *
    +
    +
    + <%~set countries = Plugins::GForum::ForumTrainingRegister::country_codes%> + +
    +
    +
    +
    Sex *
    +
    +
    + +
    +
    +
    +
    Units of Measure *
    +
    +
    + +
    +
    +
    +
    + As a mischief-control tactic, name a specific race associated with the sport this site covers that interests you* +
    +
    +
    + +
    +
    +
    +
    Would you like to receive our weekly newsletter?
    +
    +
    + +
    +
    + <%~set captcha_active = Plugins::GForum::GoogleCaptcha::is_captcha_enabled()%> + <%~if captcha_active%> +
    +
    Prove you're not a robot
    +
    +
    + <%Plugins::GForum::GoogleCaptcha::get_rcaptcha_image()%> +
    +
    + <%endif%> +
    +
    +
    +
    + +

    Note: Fields marked with * are required for validation

    +
    +
    +
    +
    + +
    <%-- end content --%> +
    +
    + + <%include include_footer.html%> + +
    + +<%~include include_global_js.html%> + + diff --git a/site/forum/user_signup_success.html b/site/forum/user_signup_success.html new file mode 100644 index 0000000..3b4c582 --- /dev/null +++ b/site/forum/user_signup_success.html @@ -0,0 +1,61 @@ + + + + <%cat_full_name%>: Sign successful +<%include include_common_head.html%> + + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = 'Signup Successful'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + + <%~if require_signup_email_validation%> +

    + You have been sent an e-mail to <%user_email%>. The e-mail contains a URL + that you must click to validate your signup. +

    + <%~if require_signup_admin_validation%> +

    + After you have gone to the URL in the e-mail the forum administrator will + have to enable your account. Once the admin has validated your account, + you may start posting messages to the forum. +

    + <%~endif%> + <%~elsif require_signup_admin_validation%> +

    + Your signup request has been received. It must be reviewed by the + administrator before being enabled. +

    + <%~else%> +

    + Congratulations, your account is now active! + Click here to + log in. +

    + <%~endif%> + +
    <%-- end content --%> +
    +
    + + <%include include_footer.html%> + +
    + +<%~include include_global_js.html%> + diff --git a/site/forum/user_validate_success.html b/site/forum/user_validate_success.html new file mode 100644 index 0000000..59e5400 --- /dev/null +++ b/site/forum/user_validate_success.html @@ -0,0 +1,57 @@ + + + + <%cat_full_name%>: Validation successful +<%include include_common_head.html%> + + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = 'Validation Successful'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> +

    Your validation was successful!

    + + <%~if require_signup_admin_validation%> + <%~if user_admin_validated%> +

    The administrator has validated your account and the account is now active.

    + <%~else%> +

    + The administrator has not yet validated your account. You will be + notified by e-mail when the account has been validated. +

    + <%~endif%> + <%~endif%> + + <%~if user_admin_validated%> +

    + You can log in by clicking + here. You + should review your settings when you log in by clicking the "Settings" + link and updating information about yourself. +

    + <%~endif%> + +
    <%-- end content --%> +
    +
    + + <%include include_footer.html%> + +
    + +<%~include include_global_js.html%> + diff --git a/site/forum/user_view.html b/site/forum/user_view.html new file mode 100644 index 0000000..9e34b96 --- /dev/null +++ b/site/forum/user_view.html @@ -0,0 +1,386 @@ +<%~if in.action eq 'bike_form'%> + + <%~if in.formsubmit%><%-- form submit --%> + <%~set res = Plugins::GForum::Bikes::bike_submit %> + <%~if res.error%>Error occurred: <%res.error%><%endparse%><%endif%> + <%~if in.bike_add%><%set success = "The bike was added"%><%elsif in.bike_edit%><%set success = "The bike was updated"%><%elsif in.delete_it%><%set success = "The bike was deleted"%><%endif%> + + <%~if success%> +<%include include_bike_list.html%> + <%endparse%> + <%~endif%> + <%~endif%> + + <%~set select_bike_id = $form.select_bike_id%> + <%~if select_bike_id%> + <%~Plugins::GForum::Bikes::get_user_bikes($user_id)%> + <%~loop user_bikes_loop%> + <%if bike_id == $select_bike_id%> +<%include include_bike_form.html%> + <%lastloop%> + <%endif%> + <%~endloop%> + <%~else%> +<%include include_bike_form.html%> + <%~endif%> + <%~endparse%> +<%~endif%> + + + + + <%cat_full_name%>: User Profile: <%user_username%> + <%include include_common_head.html%> + + +
    + <%include include_header.html%> + +
    + <%~set content_header_title = $user_username%> + + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = $user_username%> + <%~set url_user_username = escape_url unescape_html $user_username%> + +
    + + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> +
    <%hidden_form%>
    + +
    + + +
    + +
    + + +
    + +

    Bike Fit Manager

    +
    + + +
    + + <%~if current_user_id%> + <%Plugins::GForum::SlideShow::change_logs($user_id)%> + <%~if username_log_loop.length%> +

    Change Logs

    +
    + <%~loop username_log_loop%> +
    +
    <%GT::Date::date_transform($Log_time,'%yyyy%-%mm%-%dd% %HH%:%MM%:%ss%','%yyyy%-%mm%-%dd%')%>
    +
    <%Log_old_username%>
    +
    <%Log_new_username%>
    +
    + <%~endloop%> +
    + <%~endif%> + <%endif%> + +
    <%-- end content --%> +
    + +
    + <%include include_footer.html%> +
    + +<%~include include_global_js.html%> + + + diff --git a/site/forum/validation_resent.html b/site/forum/validation_resent.html new file mode 100644 index 0000000..6511bfe --- /dev/null +++ b/site/forum/validation_resent.html @@ -0,0 +1,37 @@ + + + + <%cat_full_name%>: Validation resent! + <%include include_common_head.html%> + + +
    + <%include include_header.html%> + +
    + <%~set content_header_title = 'Validation resent'%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = $content_header_title%> + +
    + + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> +

    + Your validation code has been e-mailed to you again. The e-mail you + receive will contain a link to click on to validate your account. +

    +
    <%-- end content --%> +
    +
    + + <%include include_footer.html%> +
    + +<%~include include_global_js.html%> + diff --git a/site/forum/watch_thread.html b/site/forum/watch_thread.html new file mode 100644 index 0000000..8ae81e8 --- /dev/null +++ b/site/forum/watch_thread.html @@ -0,0 +1,64 @@ + +<%~set search_widget = 1%> + + + <%cat_full_name%>: <%forum_name%>: <%post_subject%>: Thread Watched! + + <%include include_common_head.html%> + + +<%~set content_header_title = 'Thread Watched'%> + + +
    + <%include include_header.html%> + +
    +
    + + + <%~set content_header_title = $forum_name%> + <%~init array breadcrumbs%><%init hash breadcrumbs.0%><%init hash breadcrumbs.1%> + <%~set breadcrumbs.0.name = $forum_name%> + <%~set breadcrumbs.0.url = GForum::SEO::url(params => "forum=$forum_id")%> + <%~set breadcrumbs.1.name = $root_post_subject%> + + <%include include_breadcrumb.html%> +
    <%-- start content--%> + <%include include_content_header.html%> + +

    + The thread <%GForum::Category::full_name_linked($cat_id, ": ")%>: + <%forum_name%>: + <%post_subject%> + <%if user_status%><%set url_username = escape_js $user_username%><%else%><%set url_username = $user_id%><%endif%> + (started by <%if user_id%><%nbsp post_username%><%else%><%nbsp post_username%><%endif%>) + has been added to your watched threads list. + Click here to see a summary of all threads you have watched. +

    +

    + You will be automatically returned to the thread shortly, or choose the forum you wish to visit: +

    + +
    + <%hidden_form%> +
    + + +
    +
    + +
    <%-- end content --%> +
    <%-- end contentwrapper --%> +
    <%-- end main --%> + + <%include include_footer.html%> +
    + + +<%~include include_global_js.html%> + diff --git a/site/forum/whos_online.html b/site/forum/whos_online.html new file mode 100644 index 0000000..738cdaa --- /dev/null +++ b/site/forum/whos_online.html @@ -0,0 +1,148 @@ + + + + <%cat_full_name%>: Who's Online +<%include include_common_head.html%> + + +<%~set content_header_title = "Who's Online"%> + + +
    + <%include include_header.html%> + +
    +
    + + + <%~init array breadcrumbs%><%init hash breadcrumbs.0%> + <%~set breadcrumbs.0.name = $content_header_title%> + + <%include include_breadcrumb.html%> + +
    <%-- start content--%> + <%include include_content_header.html%> + +

    <%online_users.length%> user<%if online_users.length != 1%>s<%endif%> and <%online_guests.length%> guest<%if online_guests.length != 1%>s<%endif%> online in the past 15 minutes.

    + +

    Buddies

    +
    +
    +
    +
    Username
    +
    + <%~if current.user_status == $ADMINISTRATOR%> +
    IP
    + <%~endif%> +
    Where
    +
    Last seen
    +
    +
    +
    + <%~loop online_buddies%> +
    +
    <%include include_username.html%>
    +
    + <%~set url_user_username = escape_url unescape_html $user_username%> + Send PM Send PM +
    + <%~if current.user_status == $ADMINISTRATOR%> +
    + <%online_ip%> + (Ban) +
    + <%~endif%> +
    <%online_action%>
    +
    <%user_last_seen_date%>
    +
    + <%~endloop%> +
    +
    + <%~unless online_buddies.length%> +

    None of your buddies are online

    + <%~endunless%> + +

    Users

    +
    +
    +
    +
    Username
    +
    Title
    + <%~if current.user_status == $ADMINISTRATOR%> +
    IP
    + <%~endif%> +
    Where
    +
    Last seen
    +
    +
    +
    + <%~loop online_users%> +
    +
    <%include include_username.html%>
    +
    <%user_title%>
    + <%~if current.user_status == $ADMINISTRATOR%> + + <%~endif%> +
    <%online_action%>
    +
    <%user_last_seen_date%>
    +
    + <%~endloop%> +
    +
    + <%~unless online_users.length%> +

    There are no users online

    + <%~endunless%> + +

    Guests

    +
    +
    +
    +
    Username
    + <%~if current.user_status == $ADMINISTRATOR%> +
    IP
    + <%~endif%> +
    Where
    +
    Last seen
    +
    +
    +
    + <%~loop online_guests%> +
    +
    Guest #<%row_num%>
    + <%~if current.user_status == $ADMINISTRATOR%> +
    + <%online_ip%> + (Ban) +
    + <%~endif%> +
    <%online_action%>
    +
    <%online_date%>
    +
    + <%~endloop%> +
    +
    + <%~unless online_guests.length%> +

    There are no guests online

    + <%~endunless%> + +
    <%-- end content --%> +
    +
    + <%include include_footer.html%> +
    + + +<%~include include_global_js.html%> +<%~if current.user_status == $ADMINISTRATOR%> + +<%~endif%> + diff --git a/site/glist/lib/GList/Plugins/Installed/SubscribersMod.tar b/site/glist/lib/GList/Plugins/Installed/SubscribersMod.tar new file mode 100644 index 0000000..e5ee577 Binary files /dev/null and b/site/glist/lib/GList/Plugins/Installed/SubscribersMod.tar differ diff --git a/site/glist/lib/GList/Plugins/plugin.cfg b/site/glist/lib/GList/Plugins/plugin.cfg new file mode 100644 index 0000000..a8a1e95 --- /dev/null +++ b/site/glist/lib/GList/Plugins/plugin.cfg @@ -0,0 +1,23 @@ +{ + 'SubscribersMod' => { + 'hooks' => [ + [ + 'lst_sub_modify', + 'PRE', + 'GList::Plugins::SubscribersMod::lst_sub_modify', + '1' + ] + ], + 'meta' => { + 'author' => 'Virginia Lo', + 'description' => '', + 'license' => 'Other', + 'prog_ver' => '1.1.1', + 'url' => 'http://www.gossamer-threads.com', + 'version' => '1' + }, + 'version' => '1' + } +}; + +# vim:syn=perl:ts=4:noet diff --git a/site/glist/lib/GT/Installer/language.cn b/site/glist/lib/GT/Installer/language.cn new file mode 100644 index 0000000..bdaa192 --- /dev/null +++ b/site/glist/lib/GT/Installer/language.cn @@ -0,0 +1,369 @@ + +%GT::Installer::LANG = ( + ERR_REQUIRED => "%s ťաC", + ERR_PATH => "| (%s) btΤW", + ERR_PATHWRITE => "LkgJؿ (%s)C]G (%s)", + ERR_PATHCREATE => "Lkإߥؿ (%s)C]G (%s)", + ERR_URLFMT => "(%s) GOT}", + ERR_FTPFMT => "(%s) GOT FTP m", + ERR_EMAILFMT => "(%s) GOT email", + ERR_SENDMAIL => "| (%s) sbtΤWεLk", + ERR_SMTP => "(%s) OĪ SMTP DW", + ERR_PERL => "V Perl | (%s) %s", + ERR_DIREXISTS => "%s sbtΤWoO@ӥؿALkΦW٫إߥؿ", + ERR_WRITEOPEN => "Lk} %s ӼgJơF]G %s", + ERR_READOPEN => "Lk} %s ŪXơF]G %s", + ERR_RENAME => "LkN %s sRW %sF]G %s", + ERR_MKDIR => "Lk mkdir %sC]G %s", + ENTER_REG => 'пJzUX', + REG_NUM => 'UX', + ENTER_SENDMAIL => 'пJΨӰeXql sendmail | SMTP DW', + MAILER => 'Mailer', + ENTER_PERL => 'пJV Perl 5 |', + PATH_PERL => 'Perl |', + CREATE_DIRS => 'إߥؿ', + INSTALL_CURRUPTED => ' +install.dat GwlaCнT{zb FTP ɮɡBϥΪO BINARY ҦCΪ̡A +zUYɥiwlaCЦA Gossamer Threads UsɮסC + +pGzݭnUAШG + http://gossamer-threads.com/scripts/support/ +', + ADMIN_PATH_ERROR => "zJܲ{ admin |", + INTRO => ' +%s Quick Install http://gossamer-threads.com +Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved +Redistribution in part or in whole strictly prohibited. + +ԲӸƽаѾ\ LICENSE +', + WELCOME => ' +wϥ %s ۰ʦw˨tΡCw˵{N| %s Yʧ@B +]wҦɮvBΥT]w Perl |C + +Ĥ@BAХJHUơC +ziHbɭJ exit quit Өw˵{ǡC +', + IS_UPGRADE => "аݱznisw˩άON{ɯšH", + ENTER_ADMIN_PATH => "\nпJܲ{ admin |", + UNARCHIVING => 'Y', + TAR_OPEN => "Lk} %sC]G %s", + TAR_READ => "q %s ŪXƮɵoͿ~CŪX %s bytesAuŪX %s.", + TAR_BINMODE => "Lk binmode %sC]G %s", + TAR_BADARGS => "LĤ޼ơ]arguments^ǤJ %sC]G %s", + TAR_CHECKSUM => "ѪR tar ɮɵo Checksum ~Co tar ɫܥiOlaɮסC\nYG %s\nChecksumG %s\nɮסG %s\n", + TAR_NOBODY => "'%s' does not have a body!", + TAR_CANTFIND => "b tar Yɸ̧䤣ɮסG '%s' C", + TAR_CHMOD => "Lk chmod %sC]G %s", + TAR_DIRFILE => "'%s' sbӥBOɮסCLkإߥؿ", + TAR_MKDIR => "Lk mkdir %sC]G %s", + TAR_RENAME => "LksRW temp ɡG '%s' tar '%s'C]G %s", + TAR_NOGZIP => "Bz .tar.gz ɮ׮ɡBݭn Compress::Zlib ҲաC", + SKIPPING_FILE => "L %s\n", + OVERWRITTING_FILE => "\L %s ", + SKIPPING_MATCHED => "bŦXؿ̲L %s \n", + BACKING_UP_FILE => "s@ %s ƥ\n", + ERR_OPENTAR => ' +Lk} install.datIw˵{ݭnŪɡCнT{ɮצsbBɮv]wTC + +~TG + %s + +pGzݭnUAШG + http://gossamer-threads.com/scripts/support/ +', + ERR_OPENTAR_UNKNOWN => ' +} tar ɮɵoͤF~G + %s + +pGzݭnUAШG +http://gossamer-threads.com/scripts/support/ +', + WE_HAVE_IT => "\nڭ̤w`FҦ\n\n", + ENTER_STARTS => "\n ENTER ӶiwˡBΫ CTRL-C ", + NOW_UNARCHIVING => ' + +w˵{NdzƬ %s iYʧ@CЭ@ߵ... +', + UPGRADE_DONE => ' + +߱zIz %s w\ɯŦ %s CwɮפwQC + +pGzݭnswˡAХѱz̪UYɤNwɮ׸YC +', + INSTALL_DONE => ' + +%s wYʧ@CwɮפwQC +pGzݭnswˡAХѱz̪UYɤYC + +ƵGקKNz̪쪺 .tar.gz ɮׯdbzؿI + +', + TELNET_ERR => '~G %s', + FIRST_SCREEN => ' + + + wϥ <%product%> <%version%> + + +
    + + + + + +
    +

     <%product%> + w +

    +
    +
    +


    + wϥ <%product%>Cw˵{N| <%product%> Yʧ@B + ]wҦɮvBΥT]w Perl |C + + <%error%> + +
      + + + <%message%> + + + + + + + + + + + + + + + + +
    + аݱzn@sw˩άON{ɯšH +
    sw
    ɯ
    ܲ{ admin |]ɯš^G
    +

     
    +
      +
    +
    +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + UPGRADE_FIRST_SCREEN => ' + + + wϥ <%product%> <%version%> + + +
    + + + + + + + +
    +

     <%product%> + w +

    +
    +
    +


    + wϥ <%product%>Cw˵{N| <%product%> Yʧ@B + ]wҦɮvBΥT]w Perl |CbiU@BeAzDHUơCj쳣wJXzw]ȡA + ˬd̬O_TC + + <%error%> +
      + + + <%upgrade_form%> +
    +

     
    +
      +
    +
    +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + UPGRADE_SECOND_SCREEN_FIRST => ' + + + wϥ <%product%> + + + + + + + + +
    +

     <%product%> + w +

    +
    +
    +


    + + {bN{YAбz@ߵԡAnC +

    +
    +
    +
    +
    +',
    +    UPGRADE_SECOND_SCREEN_SECOND => '
    +
    +
    + +
    +
    +


    <%product%> wYʧ@C + +<%install_message%> + +

    קKNz̪쪺 .tar.gz ɮׯdbzؿI + +

    pGzDAziHڭ̪䴩QװMD䴩C +<%message%> +
      +

    + +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + INSTALL_WARNING => '

    ĵiG бN install.cgi M install.dat qؿCNoɮׯdbo̱Nް_wWü{C', + INSTALL_REMOVED => '

    wɮפwQCpGzݭnswˡAХѱz̪UYɤYC', + + OVERWRITE => '\L\n', + BACKUP => 'ƥ', + SKIP => 'L', + INSTALL_FIRST_SCREEN => ' + + + wϥ <%product%> <%version%> + + +

    + + + + + + +
    +

     <%product%> + w +

    +
    +
    +


    + wϥ <%product%>Cw˵{N| <%product%>Yʧ@B]wҦɮvB + ΥT]w Perl |C biU@BeAzDHUơCj쳣wJXzw]ȡAˬd̬O_TC + + <%error%> +
    + + + <%form%> +
    +

     
    +
      +
    +
    +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + INSTALL_SECOND_SCREEN_FIRST => ' + + + wϥ <%product%> + + + + + + + + +
    +

     <%product%> + w +

    +
    +
    +


    + + {bN{YCбz@ߵԡAnC +

    +
    +
    +
    +
    +',
    +    INSTALL_SECOND_SCREEN_SECOND => '
    +
    +
    + +
    +
    +


    <%product%> wYʧ@C + +<%install_message%> + +

    קKNz̪쪺 .tar.gz ɮׯdbzؿI + +

    pGzDAziHڭ̪䴩QװMD䴩C +<%message%> +
      +

    +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + CGI_ERROR_SCREEN => ' + + + Error + + + + + + +
    +

     ~ +

    +
    +
    +


    + oͿ~G + + <%error%> +
    +

    +
    +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + INVALID_RESPONCE => "\nLĪ^ (%s)\n", +); + diff --git a/site/glist/lib/GT/Installer/language.de b/site/glist/lib/GT/Installer/language.de new file mode 100644 index 0000000..4c1e166 --- /dev/null +++ b/site/glist/lib/GT/Installer/language.de @@ -0,0 +1,1434 @@ +%GT::Installer::LANG = ( + ERR_REQUIRED => "%s darf nicht leer sein.", + ERR_PATH => "Das Verzeichnis (%s) existiert auf diesem System nicht.", + ERR_PATHWRITE => "In das Verzeichnis '%s' kann nicht geschrieben werden. Grund: %s", + ERR_PATHCREATE => "Das Verzeichnis '%s' kann nicht angelegt werden. Grund: %s", + ERR_URLFMT => "'%s' ist kein gltiges URL Format.", + ERR_FTPFMT => "'%s' ist kein gltiges FTP-URL Format.", + ERR_EMAILFMT => "'%s' ist kein gltiges Email Format.", + ERR_SENDMAIL => "Das Verzeichnis (%s) existiert auf diesem System nicht oder ist nicht ausfhrbar", + ERR_SMTP => "'%s' ist keine gltige SMTP-Server Adresse", + ERR_PERL => "Das angegebene Perl-Verzeichnis (%s) ist ungltig %s", + ERR_DIREXISTS => "Das Verzeichnis '%s' kann nicht angelegt werden. Ein Verzeichnis mit identischem Namen existiert bereits.", + ERR_WRITEOPEN => "'%s' kann zum Schreiben nicht geffnet werden. Grund: %s", + ERR_READOPEN => "'%s' kann zum Lesen nicht geffnet werden. Grund: %s", + ERR_RENAME => "'%s' kann nicht in '%s' umbenannt werden. Grund: %s", + ENTER_REG => 'Geben Sie bitte Ihre Registrierungsnummer an', + REG_NUM => 'Registrierungsnummer', + ENTER_SENDMAIL => 'Geben Sie bitte den Pfad von Sendmail oder Ihres SMTP Server an, um Emails zu versenden', + MAILER => 'Mailer', + ENTER_PERL => 'Geben Sie bitte das Perl Verzeichnis an', + PATH_PERL => 'Perl-Verzeichnis', + CREATE_DIRS => 'Verzeichnisse anlegen', + INSTALL_CURRUPTED => ' +Die Datei Install.dat scheint unvollstndig zu sein. Bitte prfen Sie, ob der FTP-Upload im BINARY Modus durchgefhrt worden ist. +Wenn Sie einen ASCII Transfer vorgenommen haben, ist Ihre Installationsdatei beschdigt. Bitte fhren Sie einen neuen Upload auf Ihren Server durch. + +Wenn Sie Hilfe bentigen, besuchen Sie bitte : + http://malinet.de oder http://gossamer-threads.com/scripts/support/ +', + INSTALL_VERSION => ' +Diese Software setzt eine Perl Version 5.004_04 oder grsser voraus. Auf Ihrem System wurde +Version %s gefunden. Bitte ndern Sie den Perl-Pfad bei Aufruf des install.cgi Skriptes +auf eine neuere Version oder wenden Sie sich fr weitere Hilfe an Ihren Provider. +', + ADMIN_PATH_ERROR => "Sie mssen den Pfad der Administration Ihrer bereits installierten Version angeben", + INTRO => ' +%s Schnell-Installation http://gossamer-threads.com +Copyright (c) 2005 Gossamer Threads Inc. All Rights Reserved +Redistribution in part or in whole strictly prohibited. + +deutsche Version +Copyright (c) 2005 malinet Informatik alle Rechte vorbehalten +Ein Weiterverkauf von Teilen oder als Ganzes ist strengstens untersagt. +', + WELCOME => ' +Willkommen zu %s. Dieses Programm wird das Archiv von %s auspacken, alle notwendigen Dateien anlegen und entsprechende Rechte setzen. + +Um mit der Installation zu beginnen, geben Sie die folgenden Informationen an. Sie knnen jederzeit den Vorgang mit "exit" oder "quit" abbrechen. +', + IS_UPGRADE => "Soll ein Upgrade einer vorhandenen Installation durchgefhrt werden", + ENTER_ADMIN_PATH => "\nBitte geben Sie das Verzeichnis der Administration an", + UNARCHIVING => 'Dekomprimierung', + TAR_OPEN => "'%s' kann nicht geffnet werden. Grund: %s", + TAR_READ => "Beim Lesen der Datei %s ist ein Fehler aufgetreten. Es wurden %s Bytes erwartet, aber nur %s Bytes sind angekommen.", + TAR_BINMODE => "'%s' kann kein Binrmodus sein. Grund: %s", + TAR_BADARGS => "Es wurden an %s ungltige Parameter bergeben. Grund: %s", + TAR_CHECKSUM => "Checksummen Fehler im TAR-Archiv aufgetreten. In den meisten Fllen ist das TAR-Archiv fehlerhaft.\nHeader: %s\nChecksumme: %s\nDatei: %s\n", + TAR_NOBODY => "Die Datei '%s' hat keine Daten!", + TAR_CANTFIND => "Die Datei '%s' kann nicht im TAR-Archiv gefunden werden.", + TAR_CHMOD => "Der Befehl chmod %s kann nicht durchgefhrt werden. Grund: %s", + TAR_DIRFILE => "Ein Verzeichnis mit dem gleichen Namen '%s' kann nicht erstellt werden!", + TAR_MKDIR => "Der Befehl mkdir %s kann nicht durchgefhrt werden. Grund: %s", + TAR_RENAME => "Die temporre Datei '%s' kann im TAR-Archiv nicht in '%s' umbenannt werden. Grund: %s", + TAR_NOGZIP => "Compress::Zlib Modul ist fr die Verwendung von .tar.gz Dateien notwendig.", + SKIPPING_FILE => "Datei %s wird bersprungen\n", + OVERWRITTING_FILE => "Datei %s wird berschrieben", + SKIPPING_MATCHED => "berspringen von %s im Verzeichnis\n", + BACKING_UP_FILE => "Backup von %s\n", + ERR_OPENTAR => ' +Die Datei install.dat kann nicht geffnet werden! Bitte prfen Sie das Vorhandensein im richtigen Verzeichnis und die Dateirechte, um die Datei lesen zu knnen. + +Folgender Fehler trat auf: + %s + +Wenn Sie Hilfe brauchen, besuchen Sie bitte die Seite: + http://malinet.de oder http://gossamer-threads.com/scripts/support/ + ', + ERR_OPENTAR_UNKNOWN => ' +Unbekannter Fehler beim Lesen des TAR-Archivs aufgetreten: + %s + +Wenn Sie Hilfe brauchen, besuchen Sie bitte die Seite: + http://malinet.de oder http://gossamer-threads.com/scripts/support/ +', + WE_HAVE_IT => "\nSie haben alle notwendigen Informationen angegeben. Die Installation der Dateien kann beginnen...\n\n", + ENTER_STARTS => "\nDrcken Sie bitte RETURN, um zu installieren oder STRG-C um abzubrechen.", + NOW_UNARCHIVING => ' + +Die Datei %s wird jetzt dekomprimiert. Alle Dateien werden in das richtige Verzeichnis geschrieben. Bitte haben Sie Geduld... +', + UPGRADE_DONE => ' + +Gratulation! Ihre Installation von %s wurde auf Version %s upgedatet. Die Installationsdateien wurden gelscht. + +Wenn Sie die Installation erneut durchfhren wollen, dekomprimieren Sie bitte erneut die Original-Datei. +', + INSTALL_DONE => ' + +%s ist nun installiert. Die Installationsdateien wurden gelscht. +Wenn Sie die Installation erneut durchfhren wollen, dekomprimieren Sie bitte erneut die Original-Datei. + +HINWEIS: Bitte lassen Sie Ihre Original-Datei (.tar.gz) nicht in Ihrem +Web-Verzeichnis! + +', + TELNET_ERR => 'Fehler: %s', + INSTALLER_CSS => <<'CSS', #> - help vim in ft=html mode + +CSS + TELNET_EULA => <<'EULA', +GOSSAMER THREADS INC. +LIZENZVEREINBARUNG FR ENDBENUTZER + +WICHTIG: LESEN SIE DIESE LIZENZVEREINBARUNG, BEVOR SIE DIE SOFTWARE INSTALLIEREN +ZUM BESSEREN VERSTNDNIS LIEFERN WIR IHNEN NACHFOLGEND EINE DEUTSCHE BERSETZUNG: + +Dieses Software-Produkt (die "Software") und die mitgelieferte Dokumentation +(die "Dokumentation") (zusammen, das "Produkt") werden nur unter Lizenz von +Gossamer Threads Inc. (nachfolgend "GTI" genannt) dem Kunden fr seinen +Gebrauch auf Grundlage dieser Vereinbarung zur Verfgung gestellt. Sie sollten +die folgende Lizenzvereinbarung sorgfltig lesen, bevor Sie das Produkt +downloaden und installieren oder die Dokumentation verwenden. Die Installation +oder die Verwendung irgendeines Teils der Software zeigt an, da Sie diese +Bedingungen annehmen. Wenn Sie mit der nachfolgenden Lizenzvereinbarung nicht +einverstanden sind, drfen Sie die Software nicht downloaden, installieren oder +benutzen und nicht "Ich habe die Lizenzvereinbarung gelesen und akzeptiert" +auswhlen. Wenn Sie das Produkt auf einem Datentrger erhalten haben, geben +Sie bitte das vollstndige Produkt inklusive Dokumentation und Verpackung +unbenutzt an Ihren Lieferanten zurck. + +1. Lizenz: GTI bewilligt Ihnen ein persnliches, nicht exklusives, nicht + bertragbares Recht, die Software in der ausfhrbaren Weise und die + Dokumentation zu nutzen, wie es in dieser Vereinbarung festgelegt ist. + Der Lizenznehmer darf das Produkt nicht vermieten, verleihen, + verteilen, verffentlichen oder anderweitig in irgendeiner Art und + Weise gewerblich verwenden. Wenn Sie das Produkt mit Ihren Rechten an + eine andere Person weitergeben mchten, mssen Sie alle Kopien auf + Ihren Computern lschen und die Verwendung beenden. GTI bewilligt Ihnen + keine Lizenz am Quellcode der Software. Diese Lizenzvereinbarung + bewilligt Ihnen weiterhin keine Rechte an Patenten, Copyrights, + Geschftsgeheimnissen, eingetragenen Warenzeichen und an allen + mglichen anderen in Bezug zum Produkt stehenden Rechte. + +2. erlaubte Benutzung: Solange diese Lizenz gltig ist, sind Sie + autorisiert, eine Installation der Software fr eine Domain zu nutzen. + Sie drfen die Software auf dem Server lschen und auf einen anderen + Server installieren, wenn Sie gewhrleisten, dass das Produkt nur auf + einem Server unter einer Domain zur gleichen Zeit genutzt wird. + +3. Backup und Urheberrechtsvermerke: Zur Sicherheit und Archivierung + drfen Sie eine Kopie der Software und der Dokumentation reproduzieren. + Jede dieser Kopien muss die Rechtshinweise und Urheberrechtsvermerke + von GTI und die seiner Lizenzgeber in gleicher Form des Originals + enthalten. Sie sind einverstanden, kein Lschen oder Verndern + irgendeines Teiles der Texte und Copyrighthinweise an lizensierter + Software oder der Dokumentation, die unter dieser Vereinbarung + bereitgestellt werden, vorzunehmen. + +4. nderungen: Sie knnen unbegrenzt nderungen an der Software fr Ihren + eigenen internen Gebrauch durchfhren. Mit der nderung kann der + Anspruch auf untersttzenden Support seitens GTI zurckgewiesen werden. + +5. BESCHRNKUNGEN DER HAFTUNG: IN KEINEM FALL IST GTI ODER DEREN PARTNER + GEGENBER IHNEN ODER JEDER MGLICHEN ANDEREN PARTEI FR DIE DIREKTEN, + INDIREKTEN, SPEZIELLEN, BEILUFIGEN ODER SONSTIGEN SCHDEN UND + FOLGESCHDEN HAFTBAR, DIE AUS DEM GEBRAUCH DER SOFTWARE, DER UNTERLAGEN + ODER IRGENDWELCHEN ABLEITUNGEN DAVON ENTSTEHEN, SELBST WENN GTI ODER + DEREN PARTNER VON DER MGLICHKEIT SOLCHER BESCHDIGUNG BENACHRICHTIGT + WORDEN IST. GTI UND PARTNER GEBEN KEINE GARANTIE AUF IRGENDWELCHE + IMPLIZIERTEN GARANTIEN DER MARKTGNGIGKEIT, AUF EIGNUNG ZU EINEM + BESTIMMTEN ZWECK UND AUF RECHTSVERLETZUNG. + +6. Besitz: Sie besttigen und stimmen darin berein, dass die Struktur, + die Reihenfolge und die Organisation der Software wertvolle + Geschftsgeheimnisse von GTI sind und dass Sie solche + Geschftsgeheimnisse vertraulich behandeln. Weiterhin besttigen Sie + und stimmen darin berein, dass Besitz und Titel des Produktes und + aller angefertigten Kopien, unabhngig ihrer Form oder Mediums, von GTI + gehalten werden. + +7. Entschdigung: Sie werden GTI und seine Partner von irgendwelchen und + allen Handlungen, Beschdigungen, Verbindlichkeiten, Kosten, + Ansprchen, verbundenen Unkosten ("Ansprche Dritter") und durch GTI + und seiner Partner verursachten Schden in jeder Hinsicht freistellen, + die aus Ihrem Gebrauch des Produktes entstehen. Dies gilt auch fr + Ansprche durch Sie oder durch irgendwelche dritten Parteien, die auf + Ihren Gebrauch der Software zurckzufhren sind. + +8. Kndigung: Die erworbenen Lizenzen sind unbefristet, es sei denn, dass + eine Beendigung der Lizenzvereinbarung, wie unten spezifiziert, in + Kraft tritt. Sie knnen die Lizenz und diese Vereinbarung jederzeit + beenden, indem Sie die Software und die Dokumentation zusammen mit + allen Kopien und verwendeten Teilen in jeder mglichen Form zerstren. + Die Lizenzen und diese Vereinbarung werden auch sofort und automatisch + ohne Nachricht beendet, wenn Sie mit irgendeiner Bedingung oder Auflage + dieser Vereinbarung nicht einverstanden sind. Nach einer solchen + Beendigung sind Sie damit einverstanden, die Software und die + Dokumentation zusammen mit allen Kopien und verwendeten Teilen in jeder + mglichen Form zu zerstren. GTI und deren Partner sind zu keiner + Rckerstattung Ihnen gegenber bei Beendigung dieser Vereinbarung aus + irgendeinem Grund verpflichtet. + +9. Fr den Gebrauch durch die Regierung: Wenn Sie die Software im Namen + der Regierung der VEREINIGTEN STAATEN erworben haben, hat die Regierung + nur "eingeschrnkte Rechte" an der Software und an der Dokumentation, + wie in Klausel 52.227 19(c)(2) der "U.S. Federal Acquisition + Regulations" definiert ist. + +10. Salvatorische Klausel / Schlussbestimmungen: Sollte eine gegenwrtige + oder zuknftige Bestimmung dieses Vertrages ganz oder teilweise + unwirksam/nichtig oder nicht durchfhrbar sein oder werden, so wird + hiervon die Gltigkeit der brigen Bestimmungen dieses Vertrages nicht + berhrt und die ungltige Klausel wird durch eine gltige, + gesetzeskonforme und durchsetzbare Klausel, die dem Sinn der ungltigen + Klausel entspricht, ersetzt. + +11. Gesamtvereinbarung: Diese Vereinbarung setzt das vollstndige + Verstndnis Ihrerseits voraus und Sie erkennen diese Vereinbarung + zwischen Ihnen und GTI an. Weiterhin werden alle vorherigen + Vereinbarungen in Bezug auf das Produkt ersetzt. GTI kann Teile dieser + Vereinbarung durch elektronische Nachricht an Sie ndern. + +12. Anwendbares Recht: Diese Vereinbarung wird durch die Gesetze der + Provinz von Britisch-Kolumbien und die Bundesgesetze von Kanada + geregelt unter Ausschluss der Kollisionsnormen und des bereinkommens + der Vereinten Nationen ber Vertrge ber den internationalen + Warenverkauf. +------------------------------------------------------------------------------- +ENGLISCHE ORIGINALFASSUNG GOSSAMER THREADS INC. +END USER PRODUCT LICENSE AGREEMENT + +IMPORTANT: READ THIS LICENSE BEFORE INSTALLING THE SOFTWARE + +This software product (the "Software") and the accompanying +documentation (the "Documentation") (together, the "Product") are +provided only under license from Gossamer Threads Inc. ("GTI") to its +customers for their use only as set forth in this Agreement. You should +carefully read the following terms and conditions before downloading, +installing and using the Software or using the Documentation. Installing +or otherwise using any part of the Software indicates that you accept +these terms and conditions. If you do not agree with the terms and +conditions of this Agreement, do not download, install or otherwise use +the Software and do not click on the "I agree" or similar button. If you +have received the Product on physical media, return the entire product +with the software and documentation unused to the supplier where you +obtained it. + +1. License: GTI grants you a personal, non exclusive, license to use the + Software in executable form and the Documentation, subject to the terms + and restrictions set forth in this Agreement. You are not permitted to + lease, rent, distribute, publish or sub license the Software or the + Documentation or to use any part of the Product in a time sharing + arrangement or in any other unauthorized manner provided that you may + transfer all your rights in the Product to another person as long as + you remove all copies from your computers and cease all use of it. GTI + does not grant you any license in the source code of the Software. + This Agreement does not grant you any rights to patents, copyrights, + trade secrets, trademarks or any other rights with respect to the + Product. + +2. Permitted Use. You are authorized to have, at any time while this + license is valid, one installation of the Software for one domain + only, and you may remove the Software from one server and install it + on another as long as it is running on only one server for one domain + only at any time. + +3. Backup and Copyright Notices. You may reproduce one copy of the + Software and the Documentation for backup or archive purposes. Any + such copies must contain GTI's and its licensors' proprietary rights + and copyright notices in the same form as on the original. You agree + not to remove or deface any portion of any legend provided on any + licensed program or documentation delivered to you under this + Agreement. + +4. Modification. You may make unlimited modifications to the Software + for your own internal use only, but any support obligations of GTI + with respect to the Software will be terminated if you do so. + +5. LIABILITY LIMITATIONS. IN NO EVENT WILL GTI BE LIABLE TO YOU OR TO + ANY OTHER PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR + CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THE SOFTWARE, THE + DOCUMENTATION OR ANY DERIVATIVES THEREOF, EVEN IF GTI HAS BEEN ADVISED + OF THE POSSIBILITY OF SUCH DAMAGE. GTI SPECIFICALLY DISCLAIMS ANY + WARRANTIES, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NON-INFRINGEMENT. THE SOFTWARE AND THE DOCUMENTATION ARE PROVIDED ON + AN "AS IS" BASIS, AND GTI HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + SUPPORT, UPDATES, ENHANCEMENTS OR MODIFICATIONS EXCEPT AS SPECIFICALLY + AGREED UPON. + +6. Ownership. You acknowledge and agree that the structure, sequence and + organization of the Software are valuable trade secrets of GTI and + that you will hold such trade secrets in confidence. You further + acknowledge and agree that ownership of and title to the Product and + all subsequent copies thereof, regardless of the form or media, are + held by GTI. + +7. Indemnity. You will indemnify and save GTI harmless from any and all + actions, damages, liabilities, charges, claims and associated expenses + ("Claims") against or incurred by GTI in any way connected with your + use of the Product, whether such Claims are by you or by any third + parties as a result of or related to your use of the Software. + +8. Termination. The licenses granted hereunder are perpetual unless + terminated earlier as specified below. You may terminate the licenses + and this Agreement at any time by destroying the Software and the + Documentation together with all copies and merged portions in any + form. The licenses and this Agreement will also terminate immediately + and automatically without notice if you fail to comply with any term + or condition of this Agreement. Upon such termination you agree to + destroy the Software and the Documentation, together with all copies + and merged portions in any form. GTI will not be liable for any + refund to you on termination of this Agreement for any reason. + +9. Government Use. If you are acquiring the Software on behalf of the + U.S. government, the Government shall have only "Restricted Rights" in + the Software and the Documentation as defined in clause 52.227 + 19(c)(2) of the U.S. Federal Acquisition Regulations. + +10. Severability. If any provision of this Agreement is found to be + invalid, illegal or unenforceable, the validity, legality and + enforceability of any of the remaining provisions shall not in any way + be affected or impaired and a valid, legal and enforceable provision + of similar intent and economic impact shall be substituted therefor. + +11. Entire Agreement: This Agreement sets forth the entire understanding + and agreement between you and GTI and supersedes all prior agreements + with respect to the Product. GTI may change the terms of this + Agreement by electronic notice to you. + +12. Governing Law. This Agreement shall be governed by the laws of the + Province of British Columbia and the federal laws of Canada applicable + therein excluding its conflicts of laws principles and excluding the + United Nations Convention on Contracts for the International Sale of + Goods. + +EULA + EULA_PROMPT => 'Akzeptieren Sie die oben stehende Lizenzvereinbarung?', + EULA_REQUIRED => 'Um mit der Installation beginnen zu knnen, +mssen Sie die Lizenzvereinbarung annehmen. + +', + HTML_EULA => <<'EULA', + + + + <%product%> <%version%> - Installation - Lizenzvereinbarung + <%css%> + + + +
    + + +
    +
    +
    + + <%if in.eula_displayed~%> +
    +
    <%GT::Installer::tpllang('eula_required')%>
    +
    + <%~endif%> + +
    +
    +
    +
    + +
    + <%product%> Installer +
    + +

    Gossamer Threads Inc.

    +

    LIZENZVEREINBARUNG FR ENDBENUTZER

    +

    WICHTIG: LESEN SIE DIESE LIZENZVEREINBARUNG, BEVOR SIE DIE SOFTWARE INSTALLIEREN
    + ZUM BESSEREN VERSTNDNIS LIEFERN WIR IHNEN NACHFOLGEND EINE DEUTSCHE BERSETZUNG:

    + +

    + Dieses Software-Produkt (die "Software") und die mitgelieferte Dokumentation (die + "Dokumentation") (zusammen, das "Produkt") werden nur unter Lizenz von Gossamer + Threads Inc. (nachfolgend "GTI" genannt) dem Kunden fr seinen Gebrauch auf Grundlage + dieser Vereinbarung zur Verfgung gestellt. Sie sollten die folgende + Lizenzvereinbarung sorgfltig lesen, bevor Sie das Produkt downloaden und installieren + oder die Dokumentation verwenden. Die Installation oder die Verwendung irgendeines + Teils der Software zeigt an, da Sie diese Bedingungen annehmen. Wenn Sie mit der + nachfolgenden Lizenzvereinbarung nicht einverstanden sind, drfen Sie die Software + nicht downloaden, installieren oder benutzen und nicht "Ich habe die + Lizenzvereinbarung gelesen und akzeptiert" auswhlen. Wenn Sie das Produkt auf einem + Datentrger erhalten haben, geben Sie bitte das vollstndige Produkt inklusive + Dokumentation und Verpackung unbenutzt an Ihren Lieferanten zurck. +

    + +
      +
    1. + Lizenz: GTI bewilligt Ihnen ein persnliches, nicht exklusives, nicht bertragbares + Recht, die Software in der ausfhrbaren Weise und die Dokumentation zu nutzen, wie + es in dieser Vereinbarung festgelegt ist. Der Lizenznehmer darf das Produkt nicht + vermieten, verleihen, verteilen, verffentlichen oder anderweitig in irgendeiner Art + und Weise gewerblich verwenden. Wenn Sie das Produkt mit Ihren Rechten an eine + andere Person weitergeben mchten, mssen Sie alle Kopien auf Ihren Computern + lschen und die Verwendung beenden. GTI bewilligt Ihnen keine Lizenz am Quellcode + der Software. Diese Lizenzvereinbarung bewilligt Ihnen weiterhin keine Rechte an + Patenten, Copyrights, Geschftsgeheimnissen, eingetragenen Warenzeichen und an allen + mglichen anderen in Bezug zum Produkt stehenden Rechte. +
    2. + +
    3. + erlaubte Benutzung: Solange diese Lizenz gltig ist, sind Sie autorisiert, eine + Installation der Software fr eine Domain zu nutzen. Sie drfen die Software auf + dem Server lschen und auf einen anderen Server installieren, wenn Sie + gewhrleisten, dass das Produkt nur auf einem Server unter einer Domain zur gleichen + Zeit genutzt wird. +
    4. + +
    5. + Backup und Urheberrechtsvermerke: Zur Sicherheit und Archivierung drfen Sie eine + Kopie der Software und der Dokumentation reproduzieren. Jede dieser Kopien muss die + Rechtshinweise und Urheberrechtsvermerke von GTI und die seiner Lizenzgeber in + gleicher Form des Originals enthalten. Sie sind einverstanden, kein Lschen oder + Verndern irgendeines Teiles der Texte und Copyrighthinweise an lizensierter + Software oder der Dokumentation, die unter dieser Vereinbarung bereitgestellt + werden, vorzunehmen. +
    6. + +
    7. + nderungen: Sie knnen unbegrenzt nderungen an der Software fr Ihren eigenen + internen Gebrauch durchfhren. Mit der nderung kann der Anspruch auf + untersttzenden Support seitens GTI zurckgewiesen werden. +
    8. + +
    9. + BESCHRNKUNGEN DER HAFTUNG: IN KEINEM FALL IST GTI ODER DEREN PARTNER GEGENBER + IHNEN ODER JEDER MGLICHEN ANDEREN PARTEI FR DIE DIREKTEN, INDIREKTEN, SPEZIELLEN, + BEILUFIGEN ODER SONSTIGEN SCHDEN UND FOLGESCHDEN HAFTBAR, DIE AUS DEM GEBRAUCH + DER SOFTWARE, DER UNTERLAGEN ODER IRGENDWELCHEN ABLEITUNGEN DAVON ENTSTEHEN, SELBST + WENN GTI ODER DEREN PARTNER VON DER MGLICHKEIT SOLCHER BESCHDIGUNG BENACHRICHTIGT + WORDEN IST. GTI UND PARTNER GEBEN KEINE GARANTIE AUF IRGENDWELCHE IMPLIZIERTEN + GARANTIEN DER MARKTGNGIGKEIT, AUF EIGNUNG ZU EINEM BESTIMMTEN ZWECK UND AUF + RECHTSVERLETZUNG. +
    10. + +
    11. + Besitz: Sie besttigen und stimmen darin berein, dass die Struktur, die Reihenfolge + und die Organisation der Software wertvolle Geschftsgeheimnisse von GTI sind und + dass Sie solche Geschftsgeheimnisse vertraulich behandeln. Weiterhin besttigen Sie + und stimmen darin berein, dass Besitz und Titel des Produktes und aller + angefertigten Kopien, unabhngig ihrer Form oder Mediums, von GTI gehalten werden. +
    12. + +
    13. + Entschdigung: Sie werden GTI und seine Partner von irgendwelchen und allen + Handlungen, Beschdigungen, Verbindlichkeiten, Kosten, Ansprchen, verbundenen + Unkosten ("Ansprche Dritter") und durch GTI und seiner Partner verursachten Schden + in jeder Hinsicht freistellen, die aus Ihrem Gebrauch des Produktes entstehen. Dies + gilt auch fr Ansprche durch Sie oder durch irgendwelche dritten Parteien, die auf + Ihren Gebrauch der Software zurckzufhren sind. +
    14. + +
    15. + Kndigung: Die erworbenen Lizenzen sind unbefristet, es sei denn, dass eine + Beendigung der Lizenzvereinbarung, wie unten spezifiziert, in Kraft tritt. Sie + knnen die Lizenz und diese Vereinbarung jederzeit beenden, indem Sie die Software + und die Dokumentation zusammen mit allen Kopien und verwendeten Teilen in jeder + mglichen Form zerstren. Die Lizenzen und diese Vereinbarung werden auch sofort und + automatisch ohne Nachricht beendet, wenn Sie mit irgendeiner Bedingung oder Auflage + dieser Vereinbarung nicht einverstanden sind. Nach einer solchen Beendigung sind Sie + damit einverstanden, die Software und die Dokumentation zusammen mit allen Kopien + und verwendeten Teilen in jeder mglichen Form zu zerstren. GTI und deren Partner + sind zu keiner Rckerstattung Ihnen gegenber bei Beendigung dieser Vereinbarung aus + irgendeinem Grund verpflichtet. +
    16. + +
    17. + Fr den Gebrauch durch die Regierung: Wenn Sie die Software im Namen der Regierung + der VEREINIGTEN STAATEN erworben haben, hat die Regierung nur "eingeschrnkte + Rechte" an der Software und an der Dokumentation, wie in Klausel 52.227 19(c)(2) der + "U.S. Federal Acquisition Regulations" definiert ist. +
    18. + +
    19. + Salvatorische Klausel / Schlussbestimmungen: Sollte eine gegenwrtige oder + zuknftige Bestimmung dieses Vertrages ganz oder teilweise unwirksam/nichtig oder + nicht durchfhrbar sein oder werden, so wird hiervon die Gltigkeit der brigen + Bestimmungen dieses Vertrages nicht berhrt und die ungltige Klausel wird durch + eine gltige, gesetzeskonforme und durchsetzbare Klausel, die dem Sinn der + ungltigen Klausel entspricht, ersetzt. +
    20. + +
    21. + Gesamtvereinbarung: Diese Vereinbarung setzt das vollstndige Verstndnis Ihrerseits + voraus und Sie erkennen diese Vereinbarung zwischen Ihnen und GTI an. Weiterhin + werden alle vorherigen Vereinbarungen in Bezug auf das Produkt ersetzt. GTI kann + Teile dieser Vereinbarung durch elektronische Nachricht an Sie ndern. +
    22. + +
    23. + Anwendbares Recht: Diese Vereinbarung wird durch die Gesetze der Provinz von + Britisch-Kolumbien und die Bundesgesetze von Kanada geregelt unter Ausschluss der + Kollisionsnormen und des bereinkommens der Vereinten Nationen ber Vertrge ber + den internationalen Warenverkauf. +
    24. +
    +
    +

    Gossamer Threads Inc. ENGLISCHE ORIGINALFASSUNG

    +

    End User Product License Agreement

    +

    IMPORTANT: READ THIS LICENSE BEFORE INSTALLING THE SOFTWARE

    + +

    + This software product (the "Software") and the accompanying + documentation (the "Documentation") (together, the "Product") are + provided only under license from Gossamer Threads Inc. ("GTI") to + its customers for their use only as set forth in this Agreement. + You should carefully read the following terms and conditions + before downloading, installing and using the Software or using + the Documentation. Installing or otherwise using any part of the + Software indicates that you accept these terms and conditions. If + you do not agree with the terms and conditions of this Agreement, + do not download, install or otherwise use the Software and do not + click on the "I agree" or similar button. If you have received + the Product on physical media, return the entire product with the + software and documentation unused to the supplier where you + obtained it. +

    + +
      +
    1. + License: GTI grants you a personal, non exclusive, license to + use the Software in executable form and the Documentation, + subject to the terms and restrictions set forth in this + Agreement. You are not permitted to lease, rent, distribute, + publish or sub license the Software or the Documentation or to + use any part of the Product in a time sharing arrangement or + in any other unauthorized manner provided that you may + transfer all your rights in the Product to another person as + long as you remove all copies from your computers and cease + all use of it. GTI does not grant you any license in the + source code of the Software. This Agreement does not grant you + any rights to patents, copyrights, trade secrets, trademarks + or any other rights with respect to the Product. +
    2. +
    3. + Permitted Use. You are authorized to have, at any time while + this license is valid, one installation of the Software for one + domain only, and you may remove the Software from one server + and install it on another as long as it is running on only one + server for one domain only at any time. +
    4. + +
    5. + Backup and Copyright Notices. You may reproduce one copy of + the Software and the Documentation for backup or archive + purposes. Any such copies must contain GTI's and its + licensors' proprietary rights and copyright notices in the same + form as on the original. You agree not to remove or deface any + portion of any legend provided on any licensed program or + documentation delivered to you under this Agreement. +
    6. + +
    7. + Modification. You may make unlimited modifications to the + Software for your own internal use only, but any support + obligations of GTI with respect to the Software will be + terminated if you do so. +
    8. + +
    9. + LIABILITY LIMITATIONS. IN NO EVENT WILL GTI BE LIABLE TO YOU + OR TO ANY OTHER PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL + OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THE + SOFTWARE, THE DOCUMENTATION OR ANY DERIVATIVES THEREOF, EVEN IF + GTI HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. GTI + SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING BUT NOT + LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. THE SOFTWARE AND + THE DOCUMENTATION ARE PROVIDED ON AN "AS IS" BASIS, AND GTI HAS + NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, + ENHANCEMENTS OR MODIFICATIONS EXCEPT AS SPECIFICALLY AGREED + UPON. +
    10. + +
    11. + Ownership. You acknowledge and agree that the structure, + sequence and organization of the Software are valuable trade + secrets of GTI and that you will hold such trade secrets in + confidence. You further acknowledge and agree that ownership of + and title to the Product and all subsequent copies thereof, + regardless of the form or media, are held by GTI. +
    12. + +
    13. + Indemnity. You will indemnify and save GTI harmless from any + and all actions, damages, liabilities, charges, claims and + associated expenses ("Claims") against or incurred by GTI in + any way connected with your use of the Product, whether such + Claims are by you or by any third parties as a result of or + related to your use of the Software. +
    14. + +
    15. + Termination. The licenses granted hereunder are perpetual + unless terminated earlier as specified below. You may terminate + the licenses and this Agreement at any time by destroying the + Software and the Documentation together with all copies and + merged portions in any form. The licenses and this Agreement + will also terminate immediately and automatically without + notice if you fail to comply with any term or condition of this + Agreement. Upon such termination you agree to destroy the + Software and the Documentation, together with all copies and + merged portions in any form. GTI will not be liable for any + refund to you on termination of this Agreement for any reason. +
    16. + +
    17. + Government Use. If you are acquiring the Software on behalf of + the U.S. government, the Government shall have only "Restricted + Rights" in the Software and the Documentation as defined in + clause 52.227 19(c)(2) of the U.S. Federal Acquisition + Regulations. +
    18. + +
    19. + Severability. If any provision of this Agreement is found to + be invalid, illegal or unenforceable, the validity, legality + and enforceability of any of the remaining provisions shall not + in any way be affected or impaired and a valid, legal and + enforceable provision of similar intent and economic impact + shall be substituted therefor. +
    20. + +
    21. + Entire Agreement: This Agreement sets forth the entire + understanding and agreement between you and GTI and supersedes + all prior agreements with respect to the Product. GTI may + change the terms of this Agreement by electronic notice to you. +
    22. + +
    23. + Governing Law. This Agreement shall be governed by the laws of + the Province of British Columbia and the federal laws of Canada + applicable therein excluding its conflicts of laws principles + and excluding the United Nations Convention on Contracts for + the International Sale of Goods. +
    24. +
    + +
    + + + <%if in.upgrade_choice and in.install_dir%> + + + <%endif%> +
    + +
    + +
    +
    + +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + + +EULA + FIRST_SCREEN => <<'FIRST_SCREEN', + + + + <%product%> <%version%> - Installation + <%css%> + + + +
    + + +
    +
    +
    + + <%if error or message~%> +
    + <%if error%>
    <%error%>
    <%endif%> + <%if message%>
    <%message%>
    <%endif%> +
    + <%~endif%> + +
    +
    +
    +
    + +
    + <%product%> Installer +
    + +

    <%product%> Installer

    + +

    + Willkommen zu <%product%>. Dieses Programm wird das Archiv von <%product%> auspacken, + alle notwendigen Dateien anlegen, entsprechende Rechte setzen. +

    +

    Bitte wählen Sie eine der folgenden Optionen:

    + +
    + + + +
    + +
    + checked="checked"<%endunless%> /> +
    +
    +
    + + +
    + checked="checked"<%endif%> /> +
    +
    +
    + +
    + value="<%in.install_dir%>"<%endif%> /> +
    +
    + +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + + +FIRST_SCREEN + UPGRADE_FIRST_SCREEN => <<'UPGRADE_FIRST_SCREEN', + + + + <%product%> <%version%> - Installation - Upgrade einer vorhandenen Installation + <%css%> + + +
    + + +
    +
    +
    + + <%if error or message~%> +
    + <%if error%>
    <%error%>
    <%endif%> + <%if message%>
    <%message%>
    <%endif%> +
    + <%~endif%> + +
    +
    +
    +
    + +
    + <%product%> Installer > + Upgrade einer vorhandenen Installation +
    + +

    Upgrade Ihrer bereits vorhandenen Installation

    + +

    + Willkommen zu <%product%>. Dieses Programm wird das Archiv von <%product%> auspacken, + und das Upgrade durchfhren. + Die Installation in Ihrem angegebenen Pfad '<%in.install_dir%>' wird + aktualisiert auf <%product%> <%version%>. +

    + +
    + + + + + +<%loop rows%> +<%if skip%><%nextloop%><%endif%> +<%if type and type eq 'message'%> +
    <%message%>
    +<%else%> +
    + +
    + +
    +
    +<%endif%> +<%endloop%> + +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + + +UPGRADE_FIRST_SCREEN + UPGRADE_SECOND_SCREEN_FIRST => <<'UPGRADE_SECOND_SCREEN_FIRST', + + + + <%product%> <%version%> - Installation - Upgrade der vorhandenen Installation beginnt + <%css%> + + +
    + + +
    +
    +
    + +
    +
    +
    +
    + +
    + <%product%> Installer > Upgrade der vorhandenen Installation beginnt > Upgrading... +
    + +

    Upgrading...

    + +

    + Das Upgrade von <%product%> auf Version <%version%> wird jetzt durchgefhrt. Bitte haben + Sie Geduld und unterbrechen Sie nicht die Installation. +

    + +
    +
    +
    +
    +
    +
    + +
    +
    +UPGRADE_SECOND_SCREEN_FIRST
    +    UPGRADE_SECOND_SCREEN_SECOND => <<'UPGRADE_SECOND_SCREEN_SECOND',
    +
    +
    + +
    + +
    + +
    +
    +
    +
    +

    Upgrade erfolgreich durchgefhrt

    + +

    + Das Upgrade von <%product%> auf Version <%version%> wurde erfolgreich durchgefhrt. +

    + +

    + HINWEIS: Bitte lassen Sie Ihre Original-Datei (.tar.gz) nicht in Ihrem Web-Verzeichnis! +

    + +

    + Wenn Sie Hilfe brauchen, besuchen Sie bitte unser Supportforum in deutsch oder eglisch. +

    +
    +
    +
    +
    +
    + +
    + + +UPGRADE_SECOND_SCREEN_SECOND + INSTALL_WARNING => '

    WARNUNG: Bitte löschen Sie die Dateien install.cgi und install.dat aus Ihrem Verzeichnis. Wenn Sie diese Dateien nicht löschen, setzen Sie sich einem hohen Sicherheitsrisiko aus!

    ', + INSTALL_REMOVED => '

    Die Installationsdateien wurden gelöscht. Wenn Sie die Installation erneut durchfhren wollen, dekomprimieren Sie bitte erneut die Original-Datei.

    ', + OVERWRITE => 'berschreiben', + BACKUP => 'Backup', + SKIP => 'berspringen', + INSTALL_FIRST_SCREEN => <<'INSTALL_FIRST_SCREEN', + + + + <%product%> <%version%> - Neue Installation + <%css%> + + +
    + + +
    +
    +
    + + <%if error or message~%> +
    + <%if error%>
    <%error%>
    <%endif%> + <%if message%>
    <%message%>
    <%endif%> +
    + <%~endif%> + +
    +
    +
    +
    + +
    + <%product%> Installer > + Neue Installation +
    + +

    Neue Installation

    + +

    + Willkommen zu <%product%>. Dieses Programm wird das Archiv von <%product%> <%version%> + auspacken und installieren. Um mit der Installation zu beginnen, geben Sie bitte die + nachfolgenden Informationen an.
    + Hinweis: Einige Standardwerte wurden bereits eingesetzt. Bitte prüfen Sie diese + Werte sorgfältig. +

    + +
    + + + + + <%~loop fields%> + <%~if type eq 'message'%> +
    <%message%>
    + <%~else%> +
    + +
    + <%~if type eq 'create_dirs%> + checked="checked"<%endif%> /> + <%~else%> + + <%~endif%> +
    +
    + <%~endif%> + <%~endloop%> + +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + + +INSTALL_FIRST_SCREEN + INSTALL_SECOND_SCREEN_FIRST => <<'INSTALL_SECOND_SCREEN_FIRST', + + + + <%product%> <%version%> - Neue Installation beginnt + <%css%> + + +
    + + +
    +
    +
    + +
    +
    +
    +
    + +
    + <%product%> Installer > Neue Installation beginnt > Installation... +
    + +

    Installation...

    + +

    + <%product%> <%version%> wird nun vollstndig installiert. Bitte haben Sie Geduld + und unterbrechen Sie nicht die Installation. +

    + +
    +
    +
    +
    +
    +
    + +
    +
    +INSTALL_SECOND_SCREEN_FIRST
    +    INSTALL_SECOND_SCREEN_SECOND => <<'INSTALL_SECOND_SCREEN_SECOND',
    +
    +
    + +
    + +
    + +
    +
    +
    +
    +

    Installation abgeschlossen

    + +

    + <%product%> <%version%> wurde erfolgreich installiert. +

    + + <%if install_message%>

    <%install_message%>

    <%endif%> + +

    + HINWEIS: Bitte lassen Sie Ihre Original-Datei (.tar.gz) nicht in Ihrem Web-Verzeichnis! +

    + + class="no-bmargin"<%endif%>> + Wenn Sie Hilfe brauchen, besuchen Sie bitte unser Supportforum in deutsch oder eglisch. +

    + + <%if message%>

    <%message%>

    <%endif%> + +
    +
    +
    +
    +
    + +
    + + +INSTALL_SECOND_SCREEN_SECOND + CGI_ERROR_SCREEN => <<'CGI_ERROR_SCREEN', +<%if error_breakout%><%error_breakout%><%endif%> +
    + +
    + +
    +
    +
    +
    +

    Fehler

    + +

    + Bei der Installation trat folgender Fehler auf: +

    + +

    + <%error%> +

    +
    +
    +
    +
    +
    + +
    + + +CGI_ERROR_SCREEN + INVALID_RESPONCE => "\nUngltige Antwort (%s)\n", +); + +# vim:ft=perl diff --git a/site/glist/lib/GT/Installer/language.en b/site/glist/lib/GT/Installer/language.en new file mode 100644 index 0000000..d2571e0 --- /dev/null +++ b/site/glist/lib/GT/Installer/language.en @@ -0,0 +1,1187 @@ +%GT::Installer::LANG = ( + ERR_REQUIRED => "%s cannot be left blank.", + ERR_PATH => "The path (%s) does not exist on this system", + ERR_PATHWRITE => "Unable to write to directory '%s': %s", + ERR_PATHCREATE => "Unable to create directory '%s': %s", + ERR_URLFMT => "'%s' is not a valid URL", + ERR_FTPFMT => "'%s' is not a valid FTP URL", + ERR_EMAILFMT => "'%s' is not a valid e-mail address", + ERR_SENDMAIL => "The sendmail path (%s) does not exist on your system or is not executable", + ERR_SMTP => "'%s' is not a valid SMTP server address", + ERR_PERL => "The path to perl you specified (%s) %s", + ERR_DIREXISTS => "Directory '%s' cannot be created; a file/directory with the same name already exists", + ERR_WRITEOPEN => "Could not write to '%s': %s\n", + ERR_READOPEN => "Could not read '%s': %s\n", + ERR_RENAME => "Could not rename '%s' to '%s': %s\n", + ENTER_REG => 'Please enter your registration number', + REG_NUM => 'Registration Number', + ENTER_SENDMAIL => 'Please enter either a path to sendmail, or an SMTP server to use for sending mail', + MAILER => 'Mailer', + ENTER_PERL => 'Please enter the path to perl', + PATH_PERL => 'Path to perl', + CREATE_DIRS => 'Create Directories', + INSTALL_CURRUPTED => ' +install.dat appears to be corrupted. If you are using FTP to transer the file +be sure to upload the file in BINARY mode. + +If you need assistance, please visit: + http://www.gossamer-threads.com/scripts/support/ +', + INSTALL_VERSION => ' +This program requires Perl version 5.004_04 or greater to run. Your +system is only running version %s. Try changing the path to perl in +install.cgi to a newer version, or contact your ISP for help. +', + ADMIN_PATH_ERROR => "You must specify the path to the previous installation's admin area", + INTRO => ' +%s Quick Install http://www.gossamer-threads.com +Copyright (c) 2005 Gossamer Threads Inc. All Rights Reserved +Redistribution in part or in whole strictly prohibited. +', + WELCOME => ' +Welcome to %s. This program will unarchive and install %s. + +To begin, please enter the following information. Type exit +or quit at any time to abort. +', + IS_UPGRADE => "Is this an upgrade of an existing installation", + ENTER_ADMIN_PATH => "\nPlease enter path to current admin", + UNARCHIVING => 'Unarchiving', + TAR_OPEN => "Could not open '%s': %s", + TAR_READ => "There was an error reading from '%s': expected to read %s bytes, but only got %s.", + TAR_BINMODE => "Could not binmode '%s': %s", + TAR_BADARGS => "Bad arguments passed to %s: %s", + TAR_CHECKSUM => "Checksum Error parsing tar file. Most likely this is a corrupt tar.\nHeader: %s\nChecksum: %s\nFile: %s\n", + TAR_NOBODY => "File '%s' does not have a body!", + TAR_CANTFIND => "Unable to find a file named: '%s' in tar archive.", + TAR_CHMOD => "Could not chmod %s, Reason: %s", + TAR_DIRFILE => "Directory '%s' cannot be created; a file with the same name already exists", + TAR_MKDIR => "Could not mkdir %s, Reason: %s", + TAR_RENAME => "Unable to rename temp file: '%s' to tar file '%s'. Reason: %s", + TAR_NOGZIP => "Compress::Zlib module is required to work with .tar.gz files.", + SKIPPING_FILE => "Skipping %s\n", + OVERWRITTING_FILE => "Overwriting %s\n", + SKIPPING_MATCHED => "Skipping %s in matched directory\n", + BACKING_UP_FILE => "Backing up %s\n", + ERR_OPENTAR => ' +Unable to open the install.dat file! Please make sure the +file exists and that the permissions are set properly so the +program can read the file. + +The error message was: + %s + +If you need assistance, please visit: + http://www.gossamer-threads.com/scripts/support/ + ', + ERR_OPENTAR_UNKNOWN => ' +Unknown error opening tar file: + %s + +If you need assistance, please visit: +http://www.gossamer-threads.com/scripts/support/ +', + WE_HAVE_IT => "\nWe have everything we need to proceed.\n\n", + ENTER_STARTS => "\nPress ENTER to install, or CTRL-C to abort", + NOW_UNARCHIVING => ' + +We are now unarchiving %s and will be extracting +all the files shortly. Please be patient... +', + UPGRADE_DONE => ' + +Congratulations! Your copy of %s has now been +updated to version %s. The install files have +been removed. + +If you need to re-run the install, please unarchive the +original file again. +', + INSTALL_DONE => ' + +%s is now unarchived. The install files have been +removed. If you need to re-run the install, please unarchive +the original file again. + +NOTE: Please do not leave your original .tar.gz file in your +web directory! + +', + TELNET_ERR => 'Error: %s', + INSTALLER_CSS => <<'CSS', #> - help vim in ft=html mode + +CSS + TELNET_EULA => <<'EULA', +GOSSAMER THREADS INC. +END USER PRODUCT LICENSE AGREEMENT + +IMPORTANT: READ THIS LICENSE BEFORE INSTALLING THE SOFTWARE + +This software product (the "Software") and the accompanying +documentation (the "Documentation") (together, the "Product") are +provided only under license from Gossamer Threads Inc. ("GTI") to its +customers for their use only as set forth in this Agreement. You should +carefully read the following terms and conditions before downloading, +installing and using the Software or using the Documentation. Installing +or otherwise using any part of the Software indicates that you accept +these terms and conditions. If you do not agree with the terms and +conditions of this Agreement, do not download, install or otherwise use +the Software and do not click on the "I agree" or similar button. If you +have received the Product on physical media, return the entire product +with the software and documentation unused to the supplier where you +obtained it. + +1. License: GTI grants you a personal, non exclusive, license to use the + Software in executable form and the Documentation, subject to the terms + and restrictions set forth in this Agreement. You are not permitted to + lease, rent, distribute, publish or sub license the Software or the + Documentation or to use any part of the Product in a time sharing + arrangement or in any other unauthorized manner provided that you may + transfer all your rights in the Product to another person as long as + you remove all copies from your computers and cease all use of it. GTI + does not grant you any license in the source code of the Software. + This Agreement does not grant you any rights to patents, copyrights, + trade secrets, trademarks or any other rights with respect to the + Product. + +2. Permitted Use. You are authorized to have, at any time while this + license is valid, one installation of the Software for one domain + only, and you may remove the Software from one server and install it + on another as long as it is running on only one server for one domain + only at any time. + +3. Backup and Copyright Notices. You may reproduce one copy of the + Software and the Documentation for backup or archive purposes. Any + such copies must contain GTI's and its licensors' proprietary rights + and copyright notices in the same form as on the original. You agree + not to remove or deface any portion of any legend provided on any + licensed program or documentation delivered to you under this + Agreement. + +4. Modification. You may make unlimited modifications to the Software + for your own internal use only, but any support obligations of GTI + with respect to the Software will be terminated if you do so. + +5. LIABILITY LIMITATIONS. IN NO EVENT WILL GTI BE LIABLE TO YOU OR TO + ANY OTHER PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR + CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THE SOFTWARE, THE + DOCUMENTATION OR ANY DERIVATIVES THEREOF, EVEN IF GTI HAS BEEN ADVISED + OF THE POSSIBILITY OF SUCH DAMAGE. GTI SPECIFICALLY DISCLAIMS ANY + WARRANTIES, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NON-INFRINGEMENT. THE SOFTWARE AND THE DOCUMENTATION ARE PROVIDED ON + AN "AS IS" BASIS, AND GTI HAS NO OBLIGATION TO PROVIDE MAINTENANCE, + SUPPORT, UPDATES, ENHANCEMENTS OR MODIFICATIONS EXCEPT AS SPECIFICALLY + AGREED UPON. + +6. Ownership. You acknowledge and agree that the structure, sequence and + organization of the Software are valuable trade secrets of GTI and + that you will hold such trade secrets in confidence. You further + acknowledge and agree that ownership of and title to the Product and + all subsequent copies thereof, regardless of the form or media, are + held by GTI. + +7. Indemnity. You will indemnify and save GTI harmless from any and all + actions, damages, liabilities, charges, claims and associated expenses + ("Claims") against or incurred by GTI in any way connected with your + use of the Product, whether such Claims are by you or by any third + parties as a result of or related to your use of the Software. + +8. Termination. The licenses granted hereunder are perpetual unless + terminated earlier as specified below. You may terminate the licenses + and this Agreement at any time by destroying the Software and the + Documentation together with all copies and merged portions in any + form. The licenses and this Agreement will also terminate immediately + and automatically without notice if you fail to comply with any term + or condition of this Agreement. Upon such termination you agree to + destroy the Software and the Documentation, together with all copies + and merged portions in any form. GTI will not be liable for any + refund to you on termination of this Agreement for any reason. + +9. Government Use. If you are acquiring the Software on behalf of the + U.S. government, the Government shall have only "Restricted Rights" in + the Software and the Documentation as defined in clause 52.227 + 19(c)(2) of the U.S. Federal Acquisition Regulations. + +10. Severability. If any provision of this Agreement is found to be + invalid, illegal or unenforceable, the validity, legality and + enforceability of any of the remaining provisions shall not in any way + be affected or impaired and a valid, legal and enforceable provision + of similar intent and economic impact shall be substituted therefor. + +11. Entire Agreement: This Agreement sets forth the entire understanding + and agreement between you and GTI and supersedes all prior agreements + with respect to the Product. GTI may change the terms of this + Agreement by electronic notice to you. + +12. Governing Law. This Agreement shall be governed by the laws of the + Province of British Columbia and the federal laws of Canada applicable + therein excluding its conflicts of laws principles and excluding the + United Nations Convention on Contracts for the International Sale of + Goods. + +EULA + EULA_PROMPT => 'Do you accept the terms of the above license agreement?', + EULA_REQUIRED => 'You must accept the terms of the license agreement before +proceeding with the installation. + +', + HTML_EULA => <<'EULA', + + + + <%product%> <%version%> - Installation - End User Product License Agreement + <%css%> + + + +
    + + +
    +
    +
    + + <%if in.eula_displayed~%> +
    +
    <%GT::Installer::tpllang('eula_required')%>
    +
    + <%~endif%> + +
    +
    +
    +
    + +
    + <%product%> Installer +
    + +

    Gossamer Threads Inc.

    +

    End User Product License Agreement

    +

    IMPORTANT: READ THIS LICENSE BEFORE INSTALLING THE SOFTWARE

    + +

    + This software product (the "Software") and the accompanying + documentation (the "Documentation") (together, the "Product") are + provided only under license from Gossamer Threads Inc. ("GTI") to + its customers for their use only as set forth in this Agreement. + You should carefully read the following terms and conditions + before downloading, installing and using the Software or using + the Documentation. Installing or otherwise using any part of the + Software indicates that you accept these terms and conditions. If + you do not agree with the terms and conditions of this Agreement, + do not download, install or otherwise use the Software and do not + click on the "I agree" or similar button. If you have received + the Product on physical media, return the entire product with the + software and documentation unused to the supplier where you + obtained it. +

    + +
      +
    1. + License: GTI grants you a personal, non exclusive, license to + use the Software in executable form and the Documentation, + subject to the terms and restrictions set forth in this + Agreement. You are not permitted to lease, rent, distribute, + publish or sub license the Software or the Documentation or to + use any part of the Product in a time sharing arrangement or + in any other unauthorized manner provided that you may + transfer all your rights in the Product to another person as + long as you remove all copies from your computers and cease + all use of it. GTI does not grant you any license in the + source code of the Software. This Agreement does not grant you + any rights to patents, copyrights, trade secrets, trademarks + or any other rights with respect to the Product. +
    2. +
    3. + Permitted Use. You are authorized to have, at any time while + this license is valid, one installation of the Software for one + domain only, and you may remove the Software from one server + and install it on another as long as it is running on only one + server for one domain only at any time. +
    4. + +
    5. + Backup and Copyright Notices. You may reproduce one copy of + the Software and the Documentation for backup or archive + purposes. Any such copies must contain GTI's and its + licensors' proprietary rights and copyright notices in the same + form as on the original. You agree not to remove or deface any + portion of any legend provided on any licensed program or + documentation delivered to you under this Agreement. +
    6. + +
    7. + Modification. You may make unlimited modifications to the + Software for your own internal use only, but any support + obligations of GTI with respect to the Software will be + terminated if you do so. +
    8. + +
    9. + LIABILITY LIMITATIONS. IN NO EVENT WILL GTI BE LIABLE TO YOU + OR TO ANY OTHER PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL + OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THE + SOFTWARE, THE DOCUMENTATION OR ANY DERIVATIVES THEREOF, EVEN IF + GTI HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. GTI + SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING BUT NOT + LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS + FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. THE SOFTWARE AND + THE DOCUMENTATION ARE PROVIDED ON AN "AS IS" BASIS, AND GTI HAS + NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, + ENHANCEMENTS OR MODIFICATIONS EXCEPT AS SPECIFICALLY AGREED + UPON. +
    10. + +
    11. + Ownership. You acknowledge and agree that the structure, + sequence and organization of the Software are valuable trade + secrets of GTI and that you will hold such trade secrets in + confidence. You further acknowledge and agree that ownership of + and title to the Product and all subsequent copies thereof, + regardless of the form or media, are held by GTI. +
    12. + +
    13. + Indemnity. You will indemnify and save GTI harmless from any + and all actions, damages, liabilities, charges, claims and + associated expenses ("Claims") against or incurred by GTI in + any way connected with your use of the Product, whether such + Claims are by you or by any third parties as a result of or + related to your use of the Software. +
    14. + +
    15. + Termination. The licenses granted hereunder are perpetual + unless terminated earlier as specified below. You may terminate + the licenses and this Agreement at any time by destroying the + Software and the Documentation together with all copies and + merged portions in any form. The licenses and this Agreement + will also terminate immediately and automatically without + notice if you fail to comply with any term or condition of this + Agreement. Upon such termination you agree to destroy the + Software and the Documentation, together with all copies and + merged portions in any form. GTI will not be liable for any + refund to you on termination of this Agreement for any reason. +
    16. + +
    17. + Government Use. If you are acquiring the Software on behalf of + the U.S. government, the Government shall have only "Restricted + Rights" in the Software and the Documentation as defined in + clause 52.227 19(c)(2) of the U.S. Federal Acquisition + Regulations. +
    18. + +
    19. + Severability. If any provision of this Agreement is found to + be invalid, illegal or unenforceable, the validity, legality + and enforceability of any of the remaining provisions shall not + in any way be affected or impaired and a valid, legal and + enforceable provision of similar intent and economic impact + shall be substituted therefor. +
    20. + +
    21. + Entire Agreement: This Agreement sets forth the entire + understanding and agreement between you and GTI and supersedes + all prior agreements with respect to the Product. GTI may + change the terms of this Agreement by electronic notice to you. +
    22. + +
    23. + Governing Law. This Agreement shall be governed by the laws of + the Province of British Columbia and the federal laws of Canada + applicable therein excluding its conflicts of laws principles + and excluding the United Nations Convention on Contracts for + the International Sale of Goods. +
    24. +
    + +
    + + + <%if in.upgrade_choice and in.install_dir%> + + + <%endif%> +
    + +
    + +
    +
    + +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + + +EULA + FIRST_SCREEN => <<'FIRST_SCREEN', + + + + <%product%> <%version%> - Installation + <%css%> + + + +
    + + +
    +
    +
    + + <%if error or message~%> +
    + <%if error%>
    <%error%>
    <%endif%> + <%if message%>
    <%message%>
    <%endif%> +
    + <%~endif%> + +
    +
    +
    +
    + +
    + <%product%> Installer +
    + +

    <%product%> Installer

    + +

    + Welcome to <%product%>. This program will unarchive and install <%product%>. +

    + +
    + + + +
    + +
    + checked="checked"<%endunless%> /> +
    +
    +
    + + +
    + checked="checked"<%endif%> /> +
    +
    +
    + +
    + value="<%in.install_dir%>"<%endif%> /> +
    +
    + +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + + +FIRST_SCREEN + UPGRADE_FIRST_SCREEN => <<'UPGRADE_FIRST_SCREEN', + + + + <%product%> <%version%> - Installation - Upgrade existing installation + <%css%> + + +
    + + +
    +
    +
    + + <%if error or message~%> +
    + <%if error%>
    <%error%>
    <%endif%> + <%if message%>
    <%message%>
    <%endif%> +
    + <%~endif%> + +
    +
    +
    +
    + +
    + <%product%> Installer > + Upgrade existing installation +
    + +

    Upgrade existing installation

    + +

    + Welcome to <%product%>. This program will unarchive and install + your <%product%> upgrade. The installation located at the path + you provided, '<%in.install_dir%>', will be updated to + <%product%> <%version%>. +

    + +
    + + + + + +<%loop rows%> +<%if skip%><%nextloop%><%endif%> +<%if type and type eq 'message'%> +
    <%message%>
    +<%else%> +
    + +
    + +
    +
    +<%endif%> +<%endloop%> + +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + + +UPGRADE_FIRST_SCREEN + UPGRADE_SECOND_SCREEN_FIRST => <<'UPGRADE_SECOND_SCREEN_FIRST', + + + + <%product%> <%version%> - Installation - Upgrade existing installation - Upgrading + <%css%> + + +
    + + +
    +
    +
    + +
    +
    +
    +
    + +
    + <%product%> Installer > Upgrade existing installation > Upgrading... +
    + +

    Upgrading...

    + +

    + <%product%> is now being upgraded to <%version%>. Please be + patient, and do not hit stop. +

    + +
    +
    +
    +
    +
    +
    + +
    +
    +UPGRADE_SECOND_SCREEN_FIRST
    +    UPGRADE_SECOND_SCREEN_SECOND => <<'UPGRADE_SECOND_SCREEN_SECOND',
    +
    +
    + +
    + +
    + +
    +
    +
    +
    +

    Upgrade complete

    + +

    + <%product%> has been successfully upgraded to <%version%>. +

    + +

    + Please do not leave your original .tar.gz file in your web directory! +

    + +

    + If you have any problems, please visit our support forum. +

    +
    +
    +
    +
    +
    + +
    + + +UPGRADE_SECOND_SCREEN_SECOND + INSTALL_WARNING => '

    WARNING: Please remove the install.cgi and install.dat file from this directory. It is a security risk to leave those files here.

    ', + INSTALL_REMOVED => '

    The install files have been removed. If you need to re-run the install, please unarchive the original file again.

    ', + OVERWRITE => 'Overwrite', + BACKUP => 'Backup', + SKIP => 'Skip', + INSTALL_FIRST_SCREEN => <<'INSTALL_FIRST_SCREEN', + + + + <%product%> <%version%> - Installation - New installation + <%css%> + + +
    + + +
    +
    +
    + + <%if error or message~%> +
    + <%if error%>
    <%error%>
    <%endif%> + <%if message%>
    <%message%>
    <%endif%> +
    + <%~endif%> + +
    +
    +
    +
    + +
    + <%product%> Installer > + New installation +
    + +

    New installation

    + +

    + Welcome to <%product%>. This program will unarchive and install + <%product%> <%version%>. In order to proceed, you need to + provide the following information. Sensible defaults have been + chosen where possible, but please double-check that they are + correct. +

    + +
    + + + + + <%~loop fields%> + <%~if type eq 'message'%> +
    <%message%>
    + <%~else%> +
    + +
    + <%~if type eq 'create_dirs%> + checked="checked"<%endif%> /> + <%~else%> + + <%~endif%> +
    +
    + <%~endif%> + <%~endloop%> + +
    + +
    +
    + +
    +
    +
    +
    +
    + +
    + + +INSTALL_FIRST_SCREEN + INSTALL_SECOND_SCREEN_FIRST => <<'INSTALL_SECOND_SCREEN_FIRST', + + + + <%product%> <%version%> - Installation - New installation - Installing + <%css%> + + +
    + + +
    +
    +
    + +
    +
    +
    +
    + +
    + <%product%> Installer > New installation > Installing... +
    + +

    Installing...

    + +

    + <%product%> <%version%> is now being installed. Please be + patient, and do not hit stop. +

    + +
    +
    +
    +
    +
    +
    + +
    +
    +INSTALL_SECOND_SCREEN_FIRST
    +    INSTALL_SECOND_SCREEN_SECOND => <<'INSTALL_SECOND_SCREEN_SECOND',
    +
    +
    + +
    + +
    + +
    +
    +
    +
    +

    Installation complete

    + +

    + <%product%> <%version%> has been successfully installed. +

    + + <%if install_message%>

    <%install_message%>

    <%endif%> + +

    + Please do not leave your original .tar.gz file in your web directory! +

    + + class="no-bmargin"<%endif%>> + If you have any problems, please visit our support forum. +

    + + <%if message%>

    <%message%>

    <%endif%> + +
    +
    +
    +
    +
    + +
    + + +INSTALL_SECOND_SCREEN_SECOND + CGI_ERROR_SCREEN => <<'CGI_ERROR_SCREEN', +<%if error_breakout%><%error_breakout%><%endif%> +
    + +
    + +
    +
    +
    +
    +

    Error

    + +

    + An error occured: +

    + +

    + <%error%> +

    +
    +
    +
    +
    +
    + +
    + + +CGI_ERROR_SCREEN + INVALID_RESPONCE => "\nInvalid Response (%s)\n", +); + +# vim:ft=perl diff --git a/site/glist/lib/GT/Installer/language.fr b/site/glist/lib/GT/Installer/language.fr new file mode 100644 index 0000000..b3a6739 --- /dev/null +++ b/site/glist/lib/GT/Installer/language.fr @@ -0,0 +1,368 @@ + +%GT::Installer::LANG = ( + ERR_REQUIRED => "%s ne peut pas tre vide.", + ERR_PATH => "Le chemin (%s) n'existe pas sur ce systme.", + ERR_PATHWRITE => "Impossible d'crire dans le rpertoire (%s). Raison : (%s)", + ERR_PATHCREATE => "Impossible de crer le rpertoire (%s). Raison : (%s)", + ERR_URLFMT => "(%s) ne semble pas tre une URL", + ERR_FTPFMT => "(%s) ne semble pas tre une URL FTP", + ERR_EMAILFMT => "(%s) ne semble pas tre un email", + ERR_SENDMAIL => "Le chemin (%s) n'existe pas sur votre systme ou n'est pas excutable", + ERR_SMTP => "(%s) n'est pas une adresse de serveur smtp valide", + ERR_PERL => "Le chemin de Perl spcifi (%s) %s", + ERR_DIREXISTS => "%s n'est pas un rpertoire mais existe, impossible de crer un rpertoire de ce nom", + ERR_WRITEOPEN => "Impossible d'ouvrir %s pour y crire. Raison : %s", + ERR_READOPEN => "Impossible d'ouvrir %s pour le lire. Raison : %s", + ERR_RENAME => "Impossible de renommer %s par %s; Raison : %s", + ENTER_REG => 'Merci d\'entrer votre numro d\'enregistrement', + REG_NUM => 'Numro d\'enregistrement', + ENTER_SENDMAIL => 'Entrez soit le chemin de sendmail, soit un serveur SMTP utiliser pour envoyer des emails', + MAILER => 'Mailer', + ENTER_PERL => 'Entrez le chemin de Perl 5', + PATH_PERL => 'Chemin de Perl', + CREATE_DIRS => 'Cration des Rpertoires', + INSTALL_CURRUPTED => ' +install.dat semble corrompu. Soyez sr d\'avoir transfr le fichier en mode BINAIRE avec votre FTP. Ou alors vous avez peut-tre un fichier corrompu, dans ce cas vous devriez essayer de tlcharger un nouveau fichier partir de Gossamer Threads. + +Si vous avez besoin d\'aide visitez : + http://gossamer-threads.com/scripts/support/ +', + INSTALL_VERSION => ' +Ce programme requiert Perl version 5.004_04 ou plus pour fonctionner. Votre systme utilise seulement la version %s. Essayez de changer le chemin de Perl dans install.cgi pour une version suprieure, ou contactez votre hbergeur pour de l\'aide. +', + ADMIN_PATH_ERROR => "Vous devez spcifier le chemin d'installation prcdent de la zone d'Administration", + INTRO => ' +%s Installation Rapide http://gossamer-threads.com +Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved +Redistribution in part or in whole strictly prohibited. + +Lisez le fichier LICENSE pour plus de dtails. +', + WELCOME => ' +Bienvenue dans l\'auto-installation de %s. Ce programme va dcompresser le programme %s, crer tous les fichiers ncessaires, et paramtrer toutes les permissions proprement. + +Pour commencer, entrez les informations suivantes. Vous pouvez sortir tout moment pour abandonner. +', + IS_UPGRADE => "Est-ce une mise jour d'une installation existante", + ENTER_ADMIN_PATH => "\nEntrez le chemin vers l'administration actuelle", + UNARCHIVING => 'Dcompactage', + TAR_OPEN => "Impossible d'ouvrir %s. Raison: %s", + TAR_READ => "Il s'est produit une erreur en lisant %s. Nous aurions d lire %s octets, mais en avons seulement eu %s.", + TAR_BINMODE => "Impossible de binmode %s. Raison: %s", + TAR_BADARGS => "Mauvais arguments transmis %s. Raison: %s", + TAR_CHECKSUM => "Erreur de Checksum en plaant le fichier tar. Il s'agit trs probablement d'un tar corrompu.\nHeader: %s\nChecksum: %s\nFichier: %s\n", + TAR_NOBODY => "Le fichier '%s' n'a pas de corps!", + TAR_CANTFIND => "Impossible de trouver un fichier dans l'archive, nomm: '%s'.", + TAR_CHMOD => "Impossible de chmoder %s, Raison: %s", + TAR_DIRFILE => "'%s' existe et est un fichier. Impossible de crer le rpertoire", + TAR_MKDIR => "Impossible de crer %s, Raison: %s", + TAR_RENAME => "Impossible de renommer le fichier temp: '%s' par le fichier tar '%s'. Raison: %s", + TAR_NOGZIP => "Compression::Module Zlib requis pour faire fonctionner des fichiers .tar.gz.", + SKIPPING_FILE => "Ignorer %s\n", + OVERWRITTING_FILE => "Remplacer %s\n", + SKIPPING_MATCHED => "Ignorer %s dans le rpertoire trouv\n", + BACKING_UP_FILE => "Sauvegarde de %s\n", + ERR_OPENTAR => ' +Impossible d\'ouvrir le fichier install.dat! Soyez certain que le fichier existe, et que les permissions sont paramtres correctement pour que le programme lise le fichier. + +Le message d\'erreur est le suivant: + %s + +Si vous avez besoin d\'aide visitez: + http://gossamer-threads.com/scripts/support/ +', + ERR_OPENTAR_UNKNOWN => ' +Erreur inconnue en ouvrant le fichier tar: + %s + +Si vous avez besoin d\'aide visitez: +http://gossamer-threads.com/scripts/support/ +', + WE_HAVE_IT => "\nNous avons tout ce qui est ncessaire pour procder.\n\n", + ENTER_STARTS => "\nAppuyez sur ENTRE pour installer, ou CTRL-C pour abandonner", + NOW_UNARCHIVING => ' + +Nous dcompactons actuellement %s et nous dcompresserons tous les fichiers rapidement. Patientez s\'il vous plat... +', + UPGRADE_DONE => ' + +Flicitations! Votre copie de %s a t mise jour vers la version %s. Les fichiers d\'installation ont t supprims. + +Si vous devez relancer l\'installation, dcompactez le fichier original une nouvelle fois. +', + INSTALL_DONE => ' + +%s est maintenant dcompact. Les fichiers d\'installation ont t supprims. Si vous devez relancer l\'installation, dcompactez le fichier original une nouvelle fois. + +NOTE: Ne laissez pas votre fichier original .tar.gz dans votre rpertoire web! + +', + TELNET_ERR => 'Erreur: %s', + FIRST_SCREEN => ' + + + Bienvenue dans <%product%> <%version%> + + +
    + + + + + +
    +

     Installation de <%product%> + +

    +
    +
    +


    + Bienvenue dans <%product%>. Ce programme va dcompacter <%product%>, et paramtrer toutes les permissions de fichier + ainsi que le chemin de Perl correctement. + + <%error%> + +
      + + + <%message%> + + + + + + + + + + + + + + + + +
    + Merci de choisir si vous souhaitez raliser une nouvelle installation ou bien effectuer une mise jour. +
    Nouvelle Installation
    Mettre Jour une Installation xistante
    Chemin de la zone d\'administration existante:
    +

     
    +
      +
    +
    +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + UPGRADE_FIRST_SCREEN => ' + + + Bienvenue dans <%product%> <%version%> + + +
    + + + + + + + +
    +

     Installation de <%product%> + +

    +
    +
    +


    + Bienvenue dans <%product%>. Ce programme va dcompacter <%product%>, et paramtrer toutes les permissions de fichier + ainsi que le chemin de Perl correctement. Vous devez connatre les informations suivantes avant de continuer. Des paramtres par dfaut ont t choisis, mais vrifiez + qu\'ils sont bien corrects. + + <%error%> +
      + + + <%upgrade_form%> +
    +

     
    +
      +
    +
    +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + UPGRADE_SECOND_SCREEN_FIRST => ' + + + Bienvenue dans <%product%> + + + + + + + + +
    +

     Installation de <%product%> + +

    +
    +
    +


    + + Nous allons maintenant dcompacter le script, veuillez patienter s\'il vous plat, et ne pas cliquer sur Arrter. +

    +
    +
    +
    +
    +',
    +    UPGRADE_SECOND_SCREEN_SECOND => '
    +
    +
    + +
    +
    +


    <%product%> est maintenant dcompact. + +<%install_message%> + +

    Merci de ne pas laisser votre fichier .tar.gz original dans votre rpertoire web! + +

    Si vous avez un problme, visitez notre forum d\'assistance. +<%message%> +
      +

    + +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + INSTALL_WARNING => '

    ATTENTION: Supprimez les fichiers install.cgi et install.dat de ce rpertoire. Il y a un risque de scurit en les laissant ici.', + INSTALL_REMOVED => '

    Les fichiers d\'installation ont t supprims. Si vous devez relancer l\'installation, dcompactez une nouvelle fois le fichier original.', + + OVERWRITE => 'Remplacer', + BACKUP => 'Sauvegarder', + SKIP => 'Ignorer', + INSTALL_FIRST_SCREEN => ' + + + Bienvenue dans <%product%> <%version%> + + +

    + + + + + + +
    +

     Installation de <%product%> + +

    +
    +
    +


    + Bienvenue dans <%product%>. Ce programme va dcompacter <%product%>, et paramtrer toutes les permissions de fichier + ainsi que le chemin de Perl correctement. Vous devez connatre les informations suivantes avant de continuer. Des paramtres par dfaut ont t choisis, mais vrifiez + qu\'ils sont bien corrects. + + <%error%> +
    + + + <%form%> +
    +

     
    +
      +
    +
    +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + INSTALL_SECOND_SCREEN_FIRST => ' + + + Bienvenue dans <%product%> + + + + + + + + +
    +

     Installation de <%product%> + +

    +
    +
    +


    + + Nous allons maintenant dcompacter le script, veuillez patienter s\'il vous plat, et ne pas cliquer sur Arrter. +

    +
    +
    +
    +
    +',
    +    INSTALL_SECOND_SCREEN_SECOND => '
    +
    +
    + +
    +
    +


    <%product%> est maintenant dcompact. + +<%install_message%> + +

    Merci de ne pas laisser votre fichier .tar.gz original dans votre rpertoire web! + +

    Si vous avez des problmes, visitez notre forum d\'assistance. +<%message%> +
      +

    +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + CGI_ERROR_SCREEN => ' + + + Erreur + + + + + + +
    +

     Erreur +

    +
    +
    +


    + Une erreur s\'est produite: + + <%error%> +
    +

    +
    +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + INVALID_RESPONCE => "\nRponse Invalide (%s)\n", +); + diff --git a/site/glist/lib/GT/Installer/language.it b/site/glist/lib/GT/Installer/language.it new file mode 100644 index 0000000..7bfa48b --- /dev/null +++ b/site/glist/lib/GT/Installer/language.it @@ -0,0 +1,386 @@ + +%GT::Installer::LANG = ( + ERR_REQUIRED => "%s can not be left blank.", + ERR_PATH => "The path (%s) does not exist on this system", + ERR_PATHWRITE => "Unable to write to directory (%s). Reason: (%s)", + ERR_PATHCREATE => "Unable to create directory (%s). Reason: (%s)", + ERR_URLFMT => "(%s) does not look like a URL", + ERR_FTPFMT => "(%s) does not look like and FTP URL", + ERR_EMAILFMT => "(%s) does not look like an email", + ERR_SENDMAIL => "The path (%s) does not exist on your system or is not executable", + ERR_SMTP => "(%s) is not a valid smtp server address", + ERR_PERL => "The path to Perl you specified (%s) %s", + ERR_DIREXISTS => "%s is not a directory but exists, unable to make a directory of that name", + ERR_WRITEOPEN => "Could not open %s for writting; Reason: %s", + ERR_READOPEN => "Could not open %s for reading; Reason: %s", + ERR_RENAME => "Could not rename %s to %s; Reason: %s", + ENTER_REG => 'Please enter your registration number', + REG_NUM => 'Registration Number', + ENTER_SENDMAIL => 'Please enter either a path to sendmail, or a SMTP server to use for sending mail', + MAILER => 'Mailer', + ENTER_PERL => 'Please enter the path to Perl 5', + PATH_PERL => 'Path to Perl', + CREATE_DIRS => 'Create Directories', + INSTALL_CURRUPTED => ' +install.dat appears to be corrupted. Please make sure you transfer +the file in BINARY mode when using FTP. Otherwise you may have a +corrupted file, and you should try downloading a new file from +Gossamer Threads. + +If you need assistance, please visit: + http://gossamer-threads.com/scripts/support/ +', + INSTALL_VERSION => ' +This program requires Perl version 5.004_04 or greater to run. Your +system is only running version %s. Try changing the path to Perl in +install.cgi to a newer version, or contact your ISP for help. +', + ADMIN_PATH_ERROR => "You must specify the path to the previous install's admin area", + INTRO => ' +%s Quick Install http://gossamer-threads.com +Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved +Redistribution in part or in whole strictly prohibited. + +Please see LICENSE file for full details. +', + WELCOME => ' +Welcome to the %s auto install. This program will +unarchive the %s program, and create all the +files neccessary, and set all permissions properly. + +To begin, please enter the following information. Type exit or +quit at any time to abort. +', + IS_UPGRADE => "Is this an upgrade of an existing installation", + ENTER_ADMIN_PATH => "\nPlease enter path to current admin", + UNARCHIVING => 'Unarchiving', + TAR_OPEN => "Could not open %s. Reason: %s", + TAR_READ => "There was an error reading from %s. Expected to read %s bytes, but only got %s.", + TAR_BINMODE => "Could not binmode %s. Reason: %s", + TAR_BADARGS => "Bad arguments passed to %s. Reason: %s", + TAR_CHECKSUM => "Checksum Error parsing tar file. Most likely this is a corrupt tar.\nHeader: %s\nChecksum: %s\nFile: %s\n", + TAR_NOBODY => "File '%s' does not have a body!", + TAR_CANTFIND => "Unable to find a file named: '%s' in tar archive.", + TAR_CHMOD => "Could not chmod %s, Reason: %s", + TAR_DIRFILE => "'%s' exists and is a file. Cannot create directory", + TAR_MKDIR => "Could not mkdir %s, Reason: %s", + TAR_RENAME => "Unable to rename temp file: '%s' to tar file '%s'. Reason: %s", + TAR_NOGZIP => "Compress::Zlib module is required to work with .tar.gz files.", + SKIPPING_FILE => "Skipping %s\n", + OVERWRITTING_FILE => "Overwritting %s\n", + SKIPPING_MATCHED => "Skipping %s in matched directory\n", + BACKING_UP_FILE => "Backing up %s\n", + ERR_OPENTAR => ' +Unable to open the install.dat file! Please make sure the +file exists, and the permissions are set properly so the +program can read the file. + +The error message was: + %s + +If you need assistance, please visit: + http://gossamer-threads.com/scripts/support/ +', + ERR_OPENTAR_UNKNOWN => ' +Unknown error opening tar file: + %s + +If you need assistance, please visit: +http://gossamer-threads.com/scripts/support/ +', + WE_HAVE_IT => "\nWe have everything we need to proceed.\n\n", + ENTER_STARTS => "\nPress ENTER to install, or CTRL-C to abort", + NOW_UNARCHIVING => ' + +We are now unarchiving %s and will be extracting +all the files shortly. Please be patient ... +', + UPGRADE_DONE => ' + +Congratulations! Your copy of %s has now been +updated to version %s. The install files have +been removed. + +If you need to re-run the install, please unarchive the +original file again. +', + INSTALL_DONE => ' + +%s is now unarchived. The install files have been +removed. If you need to re-run the install, please unarchive +the original file again. + +NOTE: Please do not leave your original .tar.gz file in your +web directory! + +', + TELNET_ERR => 'Error: %s', + FIRST_SCREEN => ' + + + Welcome to <%product%> <%version%> + + +
    + + + + + +
    +

     <%product%> + Install +

    +
    +
    +


    + Welcome to <%product%>. This program will unarchive <%product%>, and set all the file permissions + and path to Perl properly. + + <%error%> + +
      + + + <%message%> + + + + + + + + + + + + + + + + +
    + Please select if this is a new install or an upgrade to an exiting version. +
    New Install
    Upgrade Existing Installation
    Path to Existing Installation admin area:
    +

     
    +
      +
    +
    +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + UPGRADE_FIRST_SCREEN => ' + + + Welcome to <%product%> <%version%> + + +
    + + + + + + + +
    +

     <%product%> + Install +

    +
    +
    +


    + Welcome to <%product%>. This program will unarchive <%product%>, and set all the file permissions + and path to Perl properly. You need to know the following information before continuing. Sensible defaults have been chosen, but please double check + that they are correct. + + <%error%> +
      + + + <%upgrade_form%> +
    +

     
    +
      +
    +
    +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + UPGRADE_SECOND_SCREEN_FIRST => ' + + + Welcome to <%product%> + + + + + + + + +
    +

     <%product%> + Install +

    +
    +
    +


    + + We are now going to unarchive the script, please be patient and do not hit stop. +

    +
    +
    +
    +
    +',
    +    UPGRADE_SECOND_SCREEN_SECOND => '
    +
    +
    + +
    +
    +


    <%product%> is now unarchived. + +<%install_message%> + +

    Please do not leave your original .tar.gz file in your web directory! + +

    If you have any problems, please visit our support forum. +<%message%> +
      +

    + +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + INSTALL_WARNING => '

    WARNING: Please remove the install.cgi and install.dat file from this directory. It is a security risk to leave those files here.', + INSTALL_REMOVED => '

    The install files have been removed. If you need to re-run the install, please unarchive the + original file again.', + + OVERWRITE => 'Overwrite', + BACKUP => 'Backup', + SKIP => 'Skip', + INSTALL_FIRST_SCREEN => ' + + + Welcome to <%product%> <%version%> + + +

    + + + + + + +
    +

     <%product%> + Install +

    +
    +
    +


    + Welcome to <%product%>. This program will unarchive <%product%>, and set all the file permissions + and path to Perl properly. You need to know the following information before continuing. Sensible defaults have been chosen, but please double check + that they are correct. + + <%error%> +
    + + + <%form%> +
    +

     
    +
      +
    +
    +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + INSTALL_SECOND_SCREEN_FIRST => ' + + + Welcome to <%product%> + + + + + + + + +
    +

     <%product%> + Install +

    +
    +
    +


    + + We are now going to unarchive the script, please be patient and do not hit stop. +

    +
    +
    +
    +
    +',
    +    INSTALL_SECOND_SCREEN_SECOND => '
    +
    +
    + +
    +
    +


    <%product%> is now unarchived. + +<%install_message%> + +

    Please do not leave your original .tar.gz file in your web directory! + +

    If you have any problems, please visit our support forum. +<%message%> +
      +

    +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + CGI_ERROR_SCREEN => ' + + + Error + + + + + + +
    +

     Error +

    +
    +
    +


    + An error occured: + + <%error%> +
    +

    +
    +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + INVALID_RESPONCE => "\nInvalid Responce (%s)\n", +); + diff --git a/site/glist/lib/GT/Installer/language.sp b/site/glist/lib/GT/Installer/language.sp new file mode 100644 index 0000000..da7307e --- /dev/null +++ b/site/glist/lib/GT/Installer/language.sp @@ -0,0 +1,383 @@ + +%GT::Installer::LANG = ( + ERR_REQUIRED => "%s no se puede dejar en blanco.", + ERR_PATH => "El path (%s) no existe en el sistema", + ERR_PATHWRITE => "Incapaz de escribir en el directorio (%s). Razon: (%s)", + ERR_PATHCREATE => "Incapaz de crear directorio (%s). Razon: (%s)", + ERR_URLFMT => "(%s) parece no ser un URL", + ERR_FTPFMT => "(%s) parece no ser un URL de FTP", + ERR_EMAILFMT => "(%s) parece no ser un email", + ERR_SENDMAIL => "El path (%s) no existe en su sistema o no es ejecutable", + ERR_SMTP => "(%s) no es una direccion de servidor smptp valida", + ERR_PERL => "El path a Perl usted especifico (%s) %s", + ERR_DIREXISTS => "%s no es un directorio pero existe, no se puede hacer un directorio de ese nombre", + ERR_WRITEOPEN => "No se pudo abrir %s por escritura; Razon: %s", + ERR_READOPEN => "No se pudo abrir %s por lectura; Razon: %s", + ERR_RENAME => "No se pudo renombrar %s to %s; Razon: %s", + ENTER_REG => 'Por favor ingrese su numero de registro', + REG_NUM => 'Numero de Registro', + ENTER_SENDMAIL => 'Por favor ingrese ya sea el path a sendmail, o el servidor SMTP a usar para enviar Correo', + MAILER => 'Mailer', + ENTER_PERL => 'Por favor ingrese el path a Perl 5', + PATH_PERL => 'Path a Perl', + CREATE_DIRS => 'Crear Directorios', + INSTALL_CURRUPTED => ' +install.dat parece estar corrupto. favor de asegurarse que transfiere el archivo en modo BINARIO +cuando use FTP. de otro modo usted podra obtener el archivo corrupto, y tendra que volver a bajar un nuevo archivo desde +Gossamer Threads. + +Si necesita asistencia, favor de visitar: + http://gossamer-threads.com/scripts/support/ +', + INSTALL_VERSION => ' +Este programa requiere la version Perl 5.004_04 o mas nueva para correr. Su +Sistema esta corriendo la version %s. Trate cambiando el path a Perl en +install.cgi a la version mas actual, o contacte a su ISP para ayuda. +', + ADMIN_PATH_ERROR => "Usted tiene que especificar el path al area de administracion de la instalacion previa", + INTRO => ' +%s Quick Install http://gossamer-threads.com +Copyright (c) 2004 Gossamer Threads Inc. Todos los derechos Reservados +Redistribucion en parte o total es extrictamente prohibida. + +Por favor vea el archivo de LICENCIA para detalles mas completos. +', + WELCOME => ' +Bienvenido al %s auto install. Este programa +descompactara el %s programa, y creara todos los +archivos necesarios, y pondra todos los permisos de manera propia. + +Para empezar, por favor ingrese la siguiente informacion. presione exit o +quit en cualquier momento para abortar. +', + IS_UPGRADE => "Es esta una actualizacion de una instalacion ya existente", + ENTER_ADMIN_PATH => "\npor favor ingrese el path al actual admin", + UNARCHIVING => 'Descomprimiendo', + TAR_OPEN => "No se pudo abrir %s. Razon: %s", + TAR_READ => "Hubo un error leyendo desde %s. Se suponia leyera %s bytes, pero solo leyo %s.", + TAR_BINMODE => "No se pudo modo binario %s. Razon: %s", + TAR_BADARGS => "Malos argumentos se pasaron a %s. Razon: %s", + TAR_CHECKSUM => "analisis de chequeo de archivo tar. Es muy probable este corrupto el tar.\nHeader: %s\nChecksum: %s\nFile: %s\n", + TAR_NOBODY => "Archivo '%s' no tiene contenido!", + TAR_CANTFIND => "Incapaz de encontrar un archivo llamado: '%s' en archivo tar.", + TAR_CHMOD => "No se pudo chmod %s, Razon: %s", + TAR_DIRFILE => "'%s' existe y es un archivo. No se puede crear directorio", + TAR_MKDIR => "No se pudo mkdir %s, Razon: %s", + TAR_RENAME => "No se puede renombrar el archivo temporal: '%s' to tar file '%s'. Razon: %s", + TAR_NOGZIP => "Comprimir::El modulo Zlib es requerido para trabajar con archivos .tar.gz .", + SKIPPING_FILE => "Saltandose %s\n", + OVERWRITTING_FILE => "Sobreescribiendo %s\n", + SKIPPING_MATCHED => "Saltandose %s en directorio concordante\n", + BACKING_UP_FILE => "Respaldando %s\n", + ERR_OPENTAR => ' +No se puede abrir el archivo install.dat! por favor asegurese de que +el archivo existe, y los permisos estan puestos apropiadamente y asi el programa +podra leer el archivo. + +El mensaje de error fue: + %s + +Si necesita asistencia, favor de visitar: + http://gossamer-threads.com/scripts/support/ +', + ERR_OPENTAR_UNKNOWN => ' +error desconocido al abrir el archivo tar: + %s + +Si necesita asistencia, favor de visitar: +http://gossamer-threads.com/scripts/support/ +', + WE_HAVE_IT => "\nTenemos todo lo que necesitamos para proceder.\n\n", + ENTER_STARTS => "\nPresione ENTER para instalar, o CTRL-C para abortar", + NOW_UNARCHIVING => ' + +Ahora estamos descomprimiendo %s y terminara de extraer todos los archivos +dentro de poco. Sea paciente ... +', + UPGRADE_DONE => ' + +Felicidades! Su copia de %s ha sido ya +actualizada a la version %s. Los archivos de instalacion han sido eliminados. + +Si necesita volver a correr el instalador, favor de descomprimir el archivo +original de nuevo. +', + INSTALL_DONE => ' + +%s esta ya desomprimido. Los archivos de instalacion han sido eliminados. +Si necesita volver a correr el instalador, favor de descomprimir el archivo +original de nuevo. + +NOTA: Por favor no deje el archivo original .tar.gz file en su +directorio web! + +', + TELNET_ERR => 'Error: %s', + FIRST_SCREEN => ' + + + Bienvenido a <%product%> <%version%> + + +
    + + + + + +
    +

     <%product%> + Install +

    +
    +
    +


    + Bienvenido a <%product%>. Este programa descomprimira <%product%>, y pondra todos los permisos de archivo y el path a Perl de manera propia. + + <%error%> + +
      + + + <%message%> + + + + + + + + + + + + + + + + +
    + Por favor seleccione si esta es una nueva instalacion o una actualizacion de una version existente. +
    Nueva Instalacion
    Actualizar Instalacion Existente
    Path a el area de admin de la Instalacion Existente:
    +

     
    +
      +
    +
    +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + UPGRADE_FIRST_SCREEN => ' + + + Bienvenido a <%product%> <%version%> + + +
    + + + + + + + +
    +

     <%product%> + Install +

    +
    +
    +


    + Bienvenido a <%product%>. Este programa descomprimira <%product%>, y pondra todos los permisos de archivo + y path a Perl de manera propia. Usted necesita saber la siguiente informacion antes de continuar. Los defaults sensibles han sido escogidos, pero por favor cheque de + nuevo que son correctos. + + <%error%> +
      + + + <%upgrade_form%> +
    +

     
    +
      +
    +
    +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + UPGRADE_SECOND_SCREEN_FIRST => ' + + + Welcome to <%product%> + + + + + + + + +
    +

     <%product%> + Install +

    +
    +
    +


    + + Ahora descomprimiremos el script, por favor sea paciente y no cancele ni presione stop. +

    +
    +
    +
    +
    +',
    +    UPGRADE_SECOND_SCREEN_SECOND => '
    +
    +
    + +
    +
    +


    <%product%> esta ahora descomprimido. + +<%install_message%> + +

    Por favor no deje su archivo original .tar.gz en su directorio web! + +

    Si usted tiene algun problema, por favor visite nuestro sitio de soporter . +<%message%> +
      +

    + +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + INSTALL_WARNING => '

    PRECAUCION: Por favor remueva los archivos install.cgi e install.dat de este directorio. Habra un riesgo de seguridad si los deja aqui.', + INSTALL_REMOVED => '

    Los archivos de instalacion han sido eliminados. Si usted necesita volver a correr el instalador, por favor descomprima + el archivo original de nuevo.', + + OVERWRITE => 'Sobreescribir', + BACKUP => 'Respaldar', + SKIP => 'Saltar', + INSTALL_FIRST_SCREEN => ' + + + Bienvenido a <%product%> <%version%> + + +

    + + + + + + +
    +

     <%product%> + Install +

    +
    +
    +


    + Bienvenido a <%product%>. Este programa descomprimira <%product%>, y pondra todos los permisos de archivo + y path a Perl de manera propia. Usted necesita saber la siguiente informacion antes de continuar. Los defaults sensibles han sido seleccionados, pero por favor + cheque de nuevo que son correctos. + + <%error%> +
    + + + <%form%> +
    +

     
    +
      +
    +
    +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + INSTALL_SECOND_SCREEN_FIRST => ' + + + Bienvenido a <%product%> + + + + + + + + +
    +

     <%product%> + Install +

    +
    +
    +


    + + Ahora descomprimiremos el script, por favor sea paciente y no cancele o presione stop. +

    +
    +
    +
    +
    +',
    +    INSTALL_SECOND_SCREEN_SECOND => '
    +
    +
    + +
    +
    +


    <%product%> esta ahora descomprimido. + +<%install_message%> + +

    Por favor no deje el archivo original .tar.gz en su directorio web! + +

    Si usted tiene algun problema, por favor visite nuestro sitio de soporte . +<%message%> +
      +

    +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + CGI_ERROR_SCREEN => ' + + + Error + + + + + + +
    +

     Error +

    +
    +
    +


    + Un error ha ocurrido: + + <%error%> +
    +

    +
    +

    Copyright 2004 Gossamer +Threads Inc. 

    + + +', + INVALID_RESPONCE => "\nRespuesta Invalida (%s)\n", +); + diff --git a/site/glist/lib/GT/Template/Tutorial.pod b/site/glist/lib/GT/Template/Tutorial.pod new file mode 100644 index 0000000..419e991 --- /dev/null +++ b/site/glist/lib/GT/Template/Tutorial.pod @@ -0,0 +1,1016 @@ + +=head1 NAME + +GT::Template::Tutorial - Gossamer Threads template syntax documentation + +=head1 Template Syntax + +At its most basic level, the template parser replaces tags with content. By +default a tag is anything enclosed between C%> and C<%E>. For the +purposes of this document, these two values will be assumed. + +=head2 Variable Substitution + +At the simplest level of GT::Template templates are simple variable +replacements. For example, consider the following template code: + + You are <%age%> years old. + +This would be displayed with '<%age%>' replaced with a value. For example, if +C was given a value of 35, the above would be displayed as: + + You are 35 years old. + +A more complex form of variable access is also available, which is covered +later in the +L +section of this document. + +=head2 Sets + +You can set values from within a template by using: + + <%set Title = 'Login'%> + +and now <%Title%> will be equal to Login. This is especially useful for +Cs, where you could, for example, set a Title variable to a string +that will be displayed in an included template. + +You can also set one variable to the value of another, such as: + + <%set title = $return_title%> + +This will set the variable "title" with the value of the variable "return_title." + +Note that when using " " for a string, you may include variables by using the +$variable or ${variable} syntaxes, such as: + + <%set title = "A $main_title page: ${secondary_title}Z"%> + +Additionally, the sequence "\n", "\t", and "\r" produce a linefeed character, +tab, and carriage return, respectively. Additionally, a \ may precede any +non-letter/word character to mean just the character: + + <%set price_display = "Price:\n\tTotal: \"\$3.40\""%> + +which would set price_display to: + + Price: + Total: "$3.40" + +Note that the interpolation of variables and \ escapes do *not* apply when +using 'single quotes' as the string delimiter. In such cases, only \\ and \' +have a special meaning - they are used for a literal \ and ' character. + +=head2 Operators + +GT::Template is capable of performing some basic math calculations and one +string-multiple function in templates displaying the results in the parsed template. + +For example, if the 'age' variable is 15, the following tag: + + <%age + 10%> + +will display 25 in the template. Besides addition there are the following +operators, which work as expected: + - + * + / + % (remainder) + ^ (raised to the power of) + +The following operators are also worth explaining: + + i/ + /N + ~ (Remainder difference) + x (String multiplier) + +i/ performs integral division between the two numbers. For example, '4' i/ 3 +will result in 1, '100' i/ 3 would result in 33, etc. + +/N does not actually use a literal N, instead N should be replaced by a number. +The result will be formatted (and rounded) to N decimal places. For example, +'4' /3 3 would result in: 1.333, while '5' /3 3 would give you: 1.667. +'3' /3 3 would be 1.000. + +Note that i/ and /0 are not the same, as can be illustrated here: +38 i/ '3.8' => 12 - becomes 38 i/ 3 +38 /0 '3.8' => 10 - 38 / 3.8 is calculated, then rounded with 0 decimal place +precision. + +You should be sure of which one you mean to use, or you may end up with +unexpected results. + +~ is used to get a remainder difference. Where 8 % 5 would return 3, 8 ~ 5 +will return 2. This is calculated as the divisor (5) minus the remainder (3). +This is useful when generating tables in a loop - when you hit the end of the +loop, you want to be able to put an empty cell with a colspan of however many +rows are left. Something like: <%row_num ~ 5%> will give you the proper value. + +As mentioned, there is also one string operator, 'x'. When you use 'x', the +variable (or value as we'll see in a second) will be displayed "n" times, where +"n" is the integral value of the right hand side. + +Assuming that the 'name' variable is 'Jason', this tag: + + <%name x 2%> + +will display JasonJason in the parsed template. Like this, it isn't all that +useful because you could simply put C%name%EE%name%E> in your +template. However, the right hand side may instead use the value of a +variable, such as in this example: + + <%name x $print%> + +Assuming that 'name' is still 'Jason', and that 'print' is 3, this would display: + + JasonJasonJason + +Though this is useful as is, this is taken a step furthur: the first does not +always have to be a variable. By using 'single quotation marks' or "double +quotation marks" we can display fixed text a variable number of times. + +For example: + + <%'My Text' x $print%> + +Again assuming that the variable C is 3, this will print: + + My TextMy TextMy Text + +this comes in handy when doing things like indentation. + +The same string quoting interpolation rules mentioned in L apply here - +both " and ' are accepted as delimiters, with different interpolation rules. + +=head2 Set modifiers + +You can add, subtract, etc. to your variables with the following syntax: + + <%set variable += 3%> + ++= can be changed to the following: + + += - Adds to a variable + -= - Subtracts from a variable + *= - Multiplies a variable + /= - Divides a variable + %= - Set a variable to a remainder + ^= - Raise a variable to a power + .= - Appends to a string + x= - "Multiplies" a string - "ab" x 3 is "ababab" + ||= - sets a variable if not already set + &&= - sets a variable if already set + +=head2 Set + Operators + +You can combine the above operator functions with sets (including sets with modifiers) by simply adding C to the beginning of the operator tag. For example: + + <%set foo += 3 * 3%> # foo is now set to whatever it was, + 9 + <%set foo *= 3 + 3%> # foo has been multiplied by 9 + <%set foo = $bar /0 2%> # foo is now set to half of $bar, rounded to an integer (see /N above) + +=head2 Conditionals + +You can use conditionals C, C (or C), C, and C +as in: + + <%if age%> + You are <%age%> years old. + <%elseif sex%> + You are <%sex%>. + <%else%> + I know nothing about you! + <%endif%> + + + <%ifnot login%> + You are not logged in! + <%endif%> + + + <%unless age%> + I don't know how old you are! + <%endif%> + +If you like you may use C instead of C (drop the 'e'). +C and C are aliases for C and C, respectively, +and may be used interchangeably. + +All conditionals must be ended with an C tag, although may contain any +number of C conditionals and/or a single C conditional between +the C and C tags. + +Conditionals may be nested within each other, to arbitrary depth: + + <%if age%> + You are <%age%> years old + <%if sex%> + and you are <%sex%> + <%endif%> + <%endif%> + +=head2 Comparisons + +Inside conditionals you can use C>, C>, C=>, C=>, +C<==>, C, C, C, C, C, C, C, C, C, +and C. This allows you to do things like: + + <%if age == 15%> + You're 15! + <%endif%> + +where the == can be replaced with any operator listed above. If the right hand +side of the equation starts with a '$', the string will be interpolated as a +variable. If you wish to use a string starting with a literal $, you can avoid +this interpolation by adding quotes around the right hand value. The left hand +side is always a variable. + +C, C, C, C, C, and C are the alphabetical equivelants +of C>, C>, C=>, C=>, C<==>, and C, respectively. +In terms of less-than and greater-than comparisons, the comparison is similar +to a dictionary: C is less than C, but greater than C; C<10> is +greater than C<1>, but less than C<2>; C is less than C, due to +capitalization (unless using C, C, etc.). C will be true +if the variable contains the right-hand side. C and C will be +true if the variable starts with, or ends with, respectively, the right-hand +value. + +There are also case-insensitive versions of the string comparisons - they are: +C, C, C, C, C, C, C, C, and +C. These comparisons work exactly like the versions _without_ the i +except that the comparison is case-insensitive. + +C, C, C, and C are aliases for the comparison with an +added C. C and C are deprecated aliases for C and +C and should no longer be used. + +=head2 Logical Operators + +If statements (including elseif statements) may contain multiple conditions +using one of the two booleans operators C and C. For example: + + <%if age and sex and color%> + I know your age, sex and hair color. + <%else%> + I don't have enough information about you! + <%endif%> + + <%if age < 10 or age > 90 or status eq banned%> + You are not permitted to view this page. + <%endif%> + +It should be noted that it is currently not possible to mix both C and +C in a single if statement - you may, however, use the same boolean +multiple times in a single statement. (Brackets) are also not currently +supported. + +Internally, if statements will be short-circuited as soon as possible. That +means that for the following tag: + <%if foo = 1 or foo = 2 or foo = 3%> +the following will occur: + +First, variable "foo" will be tested to see if it is numerically equal to 1. +If it is, the rest of the checks are aborted since the if will pass regardless. +If it is not, foo = 2 will be checked, and if true, will abort the next check, +and so on until a condition is true or the end of the list of statements is +encountered. + +Likewise with C, except that with C the parser will stop checking as +soon as the first false value is encountered (since a false value means the +entire condition will be false). + +=head2 Loops + +Inside your template you can use loops to loop through an array reference, code +reference, or through a fixed set of numbers. If using an array reference, each +element should be either a hash reference or a scalar value, and when using a +code reference every return should be a hash reference or scalar value, or +C to end the loop. The variables in the hash reference will then be +available for that iteration of the loop, or, if using scalar values, the value +will be available as the <%loop_value%> variable. + +For example: + + <%loop people%> + <%if name eq 'Jason'%> + I have <%color%> hair. + <%else%> + <%name%> has <%color%> hair. + <%endif%> + <%endloop%> + +would loop through all values of pens, and for each one would print the +sentence substituting the color of the pen. Also, inside your loop you can use +the following tags: + + <%row_num%> - a counter for what row is being looped, starts at 1. + <%first%> - boolean value that is true if this is the first row, false otherwise. + <%last%> - boolean value that is true if this is the last row, false otherwise. + <%inner%> - boolean value that is true if this is not first and not last. + <%even%> - boolean value is true if row_num is even. + <%odd%> - boolean value is true if row_num is odd. + +You could use even and odd tags to produce alternating colors like: + + <%loop results%> + .. + <%endloop%> + +Also, you can use <%lastloop%> to abort the loop and skip straight to the +current loop's <%endloop%> tag, and <%nextloop%> to load the next loop +variables and jump back to the beginning of the loop. + +The 6 built-in variables (row_num, first, last, ...) and any variables set via +the loop variable will only be available for the current loop iteration, after +which the variables of the next loop iteration will be set, or, for variables +that exist in one iteration but not the next, the variables that existed prior +to the loop being called will be restored. + +When using array reference-based loops (which are much more common than and +preferred to the alternative code reference-based loops), you can use the +C variable which will contain the number of items contained +within the loop. + +To loop through a particular range of numbers, you can use the following syntax: + + <%loop 1 to 5%> + ... + <%endloop%> + +This can alternatively be written as: + + <%loop 1 .. 5%> + ... + <%endloop%> + +Additionally, either or both of the two values may be a variable, such as: + + <%loop $start to $finish%> + ... + <%endloop%> + +Inside the loop, the current value is accessible via the <%loop_value%> +variable, as if you were simply looping over an array of integers. The looping +always occurs in increments of C<1>, and the start and end values will have any +fractional value truncated. + +=head2 Filters + +Filters can be used to alter the appearance of a tag. The general format of +a filter is: + + <%filtername variable%> + +Multiple filternames can be chained together as well, such as: + + <%filtername1 filtername2 filtername3 variable%> + +The filter closest to the variable name will be applied first. In the above +example, that means C will be applied, then +C applied to the result, then C applied to that +result. + +The filters available include: + +=over 4 + +=item escape_html + +This filter will perform HTML-escaping of the variable. Specifically, +C>, C>, C<&>, and C<"> are converted to their HTML equivelants +(C<<>, C<>>, C<&>, and C<">, respectively). + +Assuming that C contains the value C, the following: + + <%escape_html somevar%> + +will become: + + abc&def"ghi + +If the template parser has escape mode turned on, this would cause variables to +be escaped *twice* -- unless, of course, the variable is passed as a scalar +reference. + +=item unescape_html + +This filter will unescape the HTML escapes C<&>, C<<>, C<>>, and +C<"> back to their original forms of C>, C>, C<&>, and C<">, +respectively. + +=item escape_url + +When adding a value to a URL is necessary, C can be used to convert +characters other than alphanumeric (a-z, A-Z, 0-9) C<.>, and <-> to the +URL-escaped %XX form (where XX is a two-digit hexadecimal value). + +For example, if you want to use the tag C%name%E> in a URL, it is +recommended to instead use C%escape_url name%E>. In particular, this +avoids the possibility of the CGI parameters being passed incorrectly when a +variable contains letters such as C, C<;>, C<=>, etc. + +=item unescape_url + +This filter performs the opposite of escape_url - that is, any sequence of +C<%XX>, where C is any two hexadecimal characters, and the C<+> character +will be converted to the appropriate character - a single space in the case of +the C<+>. + +=item escape_js + +This filter will safely escape a javascript variable so that it can be used +inside a javascript string delimited with either "double quotes" or 'single +quotes'. Specifically, it puts a C<\> in front of every C<\>, C<'> and C<"> +character and converts newlines to C<\n>. + + var javascriptVariable = '<%escape_js somevar%>'; + +=item nbsp + +This filter will display the variable with all whitespace converted to HTML +non-breaking space escapes (C< >). This is particularly useful when +attempting to display something accurately which may contain spaces, or when +attempting to ensure that a value containing spaces does not wrap over multiple +lines. + +=item uc + +=item lc + +These filters display the variable with all letters changed to uppercase +(C) or lowercase (C). + +=item ucfirst + +=item lcfirst + +These filters convert the first character of the variable to uppercase +(C) or lowercase (C). + +=back + +Some filter examples, assuming the following variables have been set: + + var1 => "", + var2 => "test<b>&</b>two", + var3 => "test't
    ee", + fname => "john", + lname => "DOE" + +Examples: + + Template code --> Becomes + ============= ======= + + --> + <%unescape_html var2%> --> test&two + var jsVar = '<%escape_js var3%>'; --> var jsVar = 'test\'three'; +
    + --> + + --> + +=head2 Includes + +You can include other files. Any tags inside the includes will be evaluated as +if the content of the included file were included in the current file (although +there is one exception: unclosed if tags inside the include will be implicitly +ended). Includes can occur anywhere - inside if statements, for loops, other +includes, etc. The following tag: + + <%if info%> + <%include info.txt%> + <%else%> + <%include noinfo.txt%> + <%endif%> + +will include either the file info.txt (if info is true) or noinfo.txt (if info +is false or not set). It must be in the template's root directory which is +defined using $obj->root, or '.' by default. + +A useful application of the include tag is to include files inside a loop, as in: + + <%loop people%> + <%include person.txt%> + <%endloop%> + +Another useful example is in including a common header or footer to all pages. +If, for example, you have a header.htm that you wish to be included, but it +needs a variable title, you could combine the C with a C, such +as: + + <%set Title = 'Login'%> + <%include header.htm%> + +and then in your header.htm: + + + + <%Title%> + + +This would allow you to have different titles, but still include the same +header template on each page. + +GT::Template also supports including a file based on the value of a variable by +specifying the variable name (starting with a $) for the filename: + + <%include $var%> + +This allows for more powerful include abilities. For example, if you wanted +to have a single outer template which includes another, variable template you +could use an outer template (for the purposes of this example, assume this +template is named 'outer.html') something like: + + + <%title%> + +
    + <%include $inner_template%> +
    + + + +Then, inside other templates, you would do: + + abc.html: + ========= + + <%set inner_template = 'abc_content.html'%> + <%set title = 'ABC'%> + <%include layout.html%> + + xyz.html: + ========= + + <%set inner_template = 'xyz_content.html'%> + <%set title = 'XYZ'%> + <%include layout.html%> + +Now both abc.html and xyz.html would show up with the same page layout defined +in layout.html, with a content section coming from their respective +*_content.html templates. + +If an included template does not exist in the current template set, inherited +template sets will be checked as well. For details on template set +inheritance, see L. + +=head2 Functions + +You can call functions in either the variable substitution or in the +comparison. The function must reside in a package, and you must do the full +qualification. + + A script header normally looks like <%CGI::header%> + +which would call C. You can pass arguments to this as in: + + A script header normally looks like <%CGI::header('text/html')%>. + +Also, you can pass any currently available template variable to the function +by prefixing it with a $, such as: + + <%CGI::header($variable)%> + +Multiple arguments may be passed by comma separating the arguments, as in: + <%Mypackage::mysub($age, 'Title')%> + +If a function returns a hash reference, those values will be added to the +current substitution set. Suppose you have a function: + + package Mypackage; + sub load_globals { + .. + return { age => 15, color => red }; + } + +You could then do: + + <%Mypackage::load_globals%> + You are <%age%> years old, with <%color%> hair! + +Functions are loaded while parsing, so calling the function with different +arguments (to set your variables to different values) is possible. + +Since package names can make functions rather long and ugly, you can call +-Eparse() with an "alias" key in the options hash. This key should contain +shortcut => function pairs. For example, if you want to call Foo::Bar::blah() +in your template, you could pass: asdf => 'Foo::Bar::blah', and when <%asdf%> +or <%asdf(...)%> is encountered, Foo::Bar::blah will be called. + +=head2 Comparisons with Functions + +You can combine use a function for an if/elseif statement I, as in: + + <%if age == My::years_old%> + You are the same age as me! + <%endif%> + +which would call My::years_old() and compare the return value to the value of +the "age" variable. + +=head2 Sets with Functions + +You may use a function call as the I of a "set" instruction to set a +template variable based on the return value of the function. The following +code will set a variable named "age" to the return value of Mypackage::age(): + + <%set age = Mypackage::age%> + +Arguments passed are the same as the arguments to a regular function. + +=head2 Comments + +Comments can be used to add comments about a template, or comment out existing +sections of template code. Comments start with the template opening tag +followed by C<--> (typically C%-->) and end with C<--> followed by the +template closing tag (typically C<--%E>). Additionally, comments may +contain other template tags, including other comments. + +A simple comment: + + <%-- This is a comment, and will not be displayed --%> + +A comment example demonstrating included tags and nested comments: + + <%-- + <%template_tag%> + <%-- This is a comment, and will not be displayed <%-- another comment --%>--%> + <%if test%>example<%endif%> + --%> + +=head2 Advanced variables using references + +A more complex form of variable access is available which allows you to access +values contained within hash and/or array reference variables. These variables +can be used anywhere ordinary variables are permitted - including Cs. + +For example, assume a variable named "person" has been passed to the template +parser with a value of: + + { + name => "John Doe", + age => 35, + hair => "brown" + } + +The following example: + + <%person.name%> is <%person.age%> and has <%person.hair%> hair. + +Would display: + + John Doe is 35 and has brown hair. + +Arrays are accessed in exactly the same way, using the array index (starting +from 0). Assume for the following example that a variable C has been +provided, with the following value: + + ['(555) 555-5678', '(555) 555-6789', '(555) 555-7890'] + +The following example: + + Primary phone number: <%phone.0%> + Secondary numbers: <%loop phone%><%unless first%><%loop_value%> <%endunless%><%endloop%> + +Will display: + + Primary phone number: (555) 555-5678 + Secondary numbers: (555) 555-6789 (555) 555-7890 + +Furthurmore, the size of array reference values (such as array reference-based +loops) may be determined by adding C<.length> to the end of the tag. Using the +same "phone" value above, the following: + + Primary phone number: <%phone.0%> + <%if phone.length > 1%>Secondary numbers: <%loop phone%><%unless first%><%loop_value%> <%endunless%><%endloop%><%endif%> + +Would display the same content, however if the "phone" value contained only a +single value, the "Secondary numbers" line would not be displayed at all: + + Primary phone number: (555) 555-5678 + +Data structures of abitrary depth are supported, and can consist of any +combination of array and hash references. + +Consider the following more complex example, with a C variable set to +the following loop: + + [ + { + name => "John Doe", + age => 35, + hair => "no", + phone => { + work => "(555) 555-5678", + home => "(555) 555-6789" + } + }, + { + name => "Jane Doe", + age => 25, + hair => "brown", + phone => { + work => "(555) 555-5678", + home => "(555) 555-1234" + } + } + ] + +The following template code: + + <%loop person%><%row_num%>. <%name%>, <%age%> years of age, <%hair%> hair. Phone: work: <%phone.work%>, home: <%phone.home%>. + <%endloop%> + The first person on the list, <%person.0.name%>, can be reached at either <%person.0.phone.work%> or <%person.0.phone.home%>. + +Will display: + + 1. John Doe, 35 years of age, no hair. Phone: work: (555) 555-5678, home: (555) 555-6789. + 2. Jane Doe, 25 years of age, brown hair. Phone: work: (555) 555-5678, home: (555) 555-1234. + + The first person on the list, John Doe, can be reached at either (555) 555-5678 or (555) 555-6789. + +In addition to hash reference support, L objects and +L objects can be accessed as if they were hashes. So with: + + config => $gt_config_object, + in => $cgi_object + +The following: + + <%config.variable1%> + <%in.parameter1%> + +would access C<$gt_config_object-E{variable1}> and +C<$cgi_object-Eparam('parameter1')>. If used in a C%set ...%E> +command, the following: + + <%set config.variable1 = 4%> + <%set in.parameter1 = 5%> + +would set C<$gt_config_object-E{variable1}> to 4, and call +C<$cgi_object-Eparam('parameter1', '5')>, thereby setting it to 5. + +Furthermore, complex variable expressions may contain other variables, such as: + + <%set parameter = 'variable1'%> + <%config.$parameter%> + +which would display the same thing as C%config.variable1%E>. Note +that complex variables cannot be used here - that is: + + <%set abc.def = 'ghi'%> + <%config.$abc.def%> + +will read the value of abc, then access config.(value).def. It I, however, +possible to put a . inside a value, such as: + + <%set abc = 'def.ghi'%> + <%config.$abc%> + +would be equivalent to: + + <%config.def.ghi%> + +Complex variables can be used inside double quoted strings (wherever +double-quoted strings are accepted) but only in the form ${a.b}. Specifically, +the following: + + <%set var1.key1 = 3%><%set var2 = "$var1.key1"%> + +does not actually set var2 to "3" - the following achieves the desired effect: + + <%set var1.key1 = 3%><%set var2 = "${var1.key1}"%> + +Complex variables containing other variables are also supported, such as: + + <%set var1 = "${var1.$keyvar}"%> + +=head2 Whitespace compression + +Normally, template tags do not affect the whitespace around them, however it is +sometimes desirable or necessary to remove the whitespace immediately before or +immediately after the tag. Consider the following example, which is intended +to print the values in 'values' that are odd and less than 10, separated by +-'s: + + <%set found_one = 0%> + <%loop values%> + <%set odd = loop_value % 2%> + <%if loop_value < 10 and odd%> + <%if found_one%> + <%-- We've seen one before, so print the '-' --%> + - + <%else%> + <%-- We haven't seen one, so don't print the '-', but print it next time --%> + <%set found_one = 1%> + <%endif%> + <%loop_value%> + <%endif%> + <%endloop%> + +will actually produce, given a loopvar containing the values 1 through 15, +the following output (where _'s are actually spaces): + + + + ____ + ____ + ________ + ____________ + ____________ + ________ + ________1 + ____ + + ____ + ____ + + ____ + ____ + ________ + ____________ + ____________- + ________ + ________3 + ____ + + ____ + ____ + + ____ + ____ + ________ + ____________ + ____________- + ________ + ________5 + ____ + + ____ + ____ + + ____ + ____ + ________ + ____________ + ____________- + ________ + ________7 + ____ + + ____ + ____ + + ____ + ____ + ________ + ____________ + ____________- + ________ + ________9 + ____ + + ____ + ____ + + ____ + ____ + + ____ + ____ + + ____ + ____ + + ____ + ____ + + ____ + ____ + + +There are, of course, various ways around this - for instance, you could put +all the tags on one line with no spaces between them. However, such an +approach becomes very difficult to look at and use. The alternative is to use +GT::Templates's whitespace compression tags. + +Any tag (including comments) may start or end (or start I end) with a ~ - +if present, leading (if at the beginning of the tag) and/or trailing (if at the +end of the tag) spaces, tabs, and newline characters will be removed. So, you +could change the above code to the following: + + <%set found_one = 0 ~%> + <%loop values ~%> + + <%set odd = loop_value % 2 ~%> + <%if loop_value < 10 and odd ~%> + <%if found_one ~%> + <%-- We've seen one before, so print the '-' --~%> + - + <%~ else ~%> + <%-- We haven't seen one, so don't print the '-', but print it next time --~%> + <%set found_one = 1 ~%> + <%endif ~%> + + <%loop_value ~%> + <%endif ~%> + + <%endloop%> + +Assuming the same data, this would give you a result of: + + 1-3-5-7-9 + +The spaces around the tags are not necessary - that is, you could write +C%~tag_name ~%E> C%~ tag_name ~ %E> C% ~ tagname ~ %E> +or any other variants. The style in the example above is used only for +improved readability. + +Note that this option only affects whitespace in the current template, and does +not affect the value of variables or the content of includes. Example: + + abc <%~ varname ~%> def + +Assuming 'varname' contains the value C<' value '>, the above will produce the +output: + + abc value def + +but I: + + abcvaluedef + +Likewise with includes: + + abc <%~ include page.html ~%> def + +Assuming page.html contains the following (with a space before "a" and after +"z"): + + az + +you will get the following output: + + abc az def + +or possibly, if the include contains a newline character at the end: + + abc az + def + +but I: + + abcazdef + +Additionally, you may use the special tag C%~%E> to eliminate both +leading and trailing whitespace without needing a tag. For example: + + a + <%~%> + b + <% ~ %> + c + +will output: + + abc + +=head2 Variable Dumping + +Often it is useful to dump the value of all tags available on the current page, +or the value of a single variable. For this, there is a built-in DUMP function +to dump a variable: + + <%DUMP%> + + <%DUMP variable%> + + <%DUMP variable.key%> + +The first example will display a dump of all the tags available wherever the +C%DUMP%E> tag is added, while the second and third examples will +display a dump of the 'variable' and 'variable.key' values, respectively. + +C tags should only be used for debugging purposes; they are not intended +to be used on live, publically accessible templates as they may contain +sensitive data that should not be made publically available. + +=head1 COPYRIGHT + +Copyright (c) 2005 Gossamer Threads, Inc. All Rights Reserved. +http://www.gossamer-threads.com/ + +=head1 SEE ALSO + +L - for documentation on invoking the template parser. + +L - for documentation on template inheritance. + +=head1 VERSION + +Revision: $Id: Tutorial.pod,v 2.11 2005/04/23 23:11:33 brewt Exp $ + +=cut diff --git a/site/glist/templates/common/editor.css b/site/glist/templates/common/editor.css new file mode 100644 index 0000000..c01c533 --- /dev/null +++ b/site/glist/templates/common/editor.css @@ -0,0 +1,131 @@ +.body_content { + font-family : <%if advanced_editor_font%><%advanced_editor_font%><%else%>Verdana, Arial, Helvetica, sans-serif<%endif%>; + font-size: <%if is_ie%>x-<%endif%>small; + color : #000000; + background-color: <%if advanced_editor_background%><%advanced_editor_background%><%else%>#FFFFFF<%endif%>; + cursor: default; +} + +.html_content { + font-family : Verdana, Arial, Helvetica, sans-serif; + font-size: 14px; + color : #000000; + background-color: #FFFFFF; + cursor: default; + visibility: hidden; + border: 0px; + width: 0px; + height: 0px; + padding: 5px; +} + +.bar { + position : absolute; + background-color : <%if editor_base_color%><%editor_base_color%><%else%>#CCCCCC<%endif%>; + border-bottom : buttonshadow solid 1px; + border-left : buttonhighlight solid 1px; + border-right : buttonshadow solid 1px; + border-top : buttonhighlight solid 1px; + height : 25px; + top : 0px; + left : 0px; +} + +.toolbar { + position : relative; + background-color : <%if editor_base_color%><%editor_base_color%><%else%>#CCCCCC<%endif%>; + border-bottom : 0px; + border-left : 0px; + border-right : 0px; + border-top : 0px; + height : 25px; + top : 0px; + left : 0px; +} + +.menu { + position : absolute; + background-color : <%if editor_base_color%><%editor_base_color%><%else%>#CCCCCC<%endif%>; + border-bottom : buttonshadow solid 1px; + border-left : buttonhighlight solid 1px; + border-right : buttonshadow solid 1px; + border-top : buttonhighlight solid 1px; + height : 25px; + top : 0px; + left : 0px; + visibility : hidden; +} + +.tb_icon { + position : absolute; + left : -1px; + top : -1px; +} + +.icon_down { + position : absolute; + left : 0px; + top : 0px; +} + +.icon_downpressed { + position : absolute; + left : 1px; + top : 1px; +} + +.tb_menu_item { + position : absolute; + border-bottom : <%editor_base_color%> solid 1px; + border-left : <%editor_base_color%> solid 1px; + border-right : <%editor_base_color%> solid 1px; + border-top : <%editor_base_color%> solid 1px; + top : 1px; + height : 22px; + width : 23px; +} + +.menu_item_mouseoverup { + position : absolute; + border-bottom : buttonshadow solid 1px; + border-left : buttonhighlight solid 1px; + border-right : buttonshadow solid 1px; + border-top : buttonhighlight solid 1px; + top : 1px; + height : 22px; + width : 23px; +} + +.menu_item_mouseoverdown { + position : absolute; + border-bottom : buttonhighlight solid 1px; + border-left : buttonshadow solid 1px; + border-right : buttonhighlight solid 1px; + border-top : buttonshadow solid 1px; + top : 1px; + height : 22px; + width : 23px; +} + +.menu_item_down { + position : absolute; + background-color : gainsboro; + border-bottom : buttonhighlight solid 1px; + border-left : buttonshadow solid 1px; + border-right : buttonhighlight solid 1px; + border-top : buttonshadow solid 1px; + top : 1px; + height : 22px; + width : 23px; +} + +.tb_sep { + position : absolute; + border-left : buttonshadow solid 1px; + border-right : buttonhighlight solid 1px; + font-size : 0px; + top : 1px; + height : 22px; + width : 0px; +} + diff --git a/site/glist/templates/common/editor_button.html b/site/glist/templates/common/editor_button.html new file mode 100644 index 0000000..45d6f58 --- /dev/null +++ b/site/glist/templates/common/editor_button.html @@ -0,0 +1,89 @@ + +Button + + + + + + + + + + + + + + + + + +
    Name:
    Value/label:
    Button type: + + + + + + +
    + Normal + + Submit + + Reset +
    +
    Tab order:
    +
    +
    +   +   +
    + + diff --git a/site/glist/templates/common/editor_color.html b/site/glist/templates/common/editor_color.html new file mode 100644 index 0000000..599678c --- /dev/null +++ b/site/glist/templates/common/editor_color.html @@ -0,0 +1,409 @@ + + + Color + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
                 
                 
                 
                 
                 
                 
                 
    +
    + + + + + + +
    +
    Sample
    +
    + + + + + + +
    + + diff --git a/site/glist/templates/common/editor_dialog.css b/site/glist/templates/common/editor_dialog.css new file mode 100644 index 0000000..6f2edb6 --- /dev/null +++ b/site/glist/templates/common/editor_dialog.css @@ -0,0 +1,53 @@ +/* ================================================================== + * Gossamer Threads Module Library - http://gossamer-threads.com/ + * + * dialog + * Author : Scott Beck + * $Id: editor_dialog.css,v 1.6 2004/10/14 22:35:14 bao Exp $ + * + * Copyright (c) 2000 Gossamer Threads Inc. All Rights Reserved. + * ================================================================== + * + * Description: Style sheet for dialog pop-ups. + */ + +BODY { + font-family : Verdana, Arial, Helvetica, sans-serif; + font-size : 12px; + background-color : buttonface; + margin : 0; +} + +TD { + font-family : Verdana, Arial, Helvetica, sans-serif; + font-size : 12px; +} + +.sample { + font-family : Verdana, Arial, Helvetica, sans-serif; + border-left : buttonshadow solid 1px; + border-bottom : buttonhighlight solid 1px; + border-right : buttonhighlight solid 1px; + border-top : buttonshadow solid 1px; + overflow : hidden; + background-color : buttonface; +} + +.button { + font-family : Verdana, Arial, Helvetica, sans-serif; + background-color : buttonface; + font-size : 11px; + width : 80px; +} + +select { + font-family : Verdana, Arial, Helvetica, sans-serif; + font-size : 11px; + border-bottom : buttonhighlight solid 2px; + border-left : buttonshadow solid 2px; + border-right : buttonhighlight solid 2px; + border-top : buttonshadow solid 2px; + overflow : hidden; + cursor : default; +} + diff --git a/site/glist/templates/common/editor_editor.html b/site/glist/templates/common/editor_editor.html new file mode 100644 index 0000000..4b87475 --- /dev/null +++ b/site/glist/templates/common/editor_editor.html @@ -0,0 +1,9 @@ + + + + + + diff --git a/site/glist/templates/common/editor_font.html b/site/glist/templates/common/editor_font.html new file mode 100644 index 0000000..c569f94 --- /dev/null +++ b/site/glist/templates/common/editor_font.html @@ -0,0 +1,313 @@ + + + Select Font + + + + + + + + +
    Font:
    + + + +
     Effects 
    + +
    Underline
    + +
    Color:
    + + + +
    Style:
    + + + +
    Size:
    + + + +
     Sample 
    +
    AaBbYyZz
    + + + + + + diff --git a/site/glist/templates/common/editor_iframe.html b/site/glist/templates/common/editor_iframe.html new file mode 100644 index 0000000..83febcf --- /dev/null +++ b/site/glist/templates/common/editor_iframe.html @@ -0,0 +1,9 @@ + + + +
    + + + + + +Insert Image + + + + +
    + +
    + + diff --git a/site/glist/templates/common/editor_link.html b/site/glist/templates/common/editor_link.html new file mode 100644 index 0000000..0d92b07 --- /dev/null +++ b/site/glist/templates/common/editor_link.html @@ -0,0 +1,30 @@ + + + Create a Link + + + + +
    + + + + + +
    URL: +
    + + +
    + + diff --git a/site/glist/templates/common/editor_objects.html b/site/glist/templates/common/editor_objects.html new file mode 100644 index 0000000..0f3972b --- /dev/null +++ b/site/glist/templates/common/editor_objects.html @@ -0,0 +1,2 @@ +
    Loading...
    + diff --git a/site/glist/templates/common/editor_table.html b/site/glist/templates/common/editor_table.html new file mode 100644 index 0000000..c7becdd --- /dev/null +++ b/site/glist/templates/common/editor_table.html @@ -0,0 +1,50 @@ + + + Create Table + + + + +
    + + + + + + + + + + + + + + + + + + + +
    Rows:Cell Padding:
    Columns:Cell spacing:
    WidthBorder:
    +
    +
    + +   +
    diff --git a/site/glist/templates/common/spellcheck.css b/site/glist/templates/common/spellcheck.css new file mode 100644 index 0000000..de6d78a --- /dev/null +++ b/site/glist/templates/common/spellcheck.css @@ -0,0 +1,27 @@ +/* inline spellcheck */ +.spell { + font-family : Verdana, Arial, Helvetica, sans-serif; + font-size: 13px; + background-color: #E8F9E6; + border: 1px inset #cccccc; +} +.spell-updated { + color: #326aaf; +} +.spell-misspelled { + color: #ff0000; +} +.spell-menu { + font-family : Verdana, Arial, Helvetica, sans-serif; + font-size: 13px; + background-color: #CCCCCC; + border: 1px outset #CCCCCC; +} +.spell-menu-mouse-out { + background-color: #CCCCCC; +} +.spell-menu-mouse-over { + background-color: #797AA1; + color: #ffffff; +} + diff --git a/site/glist/templates/common/spellcheck_editor.html b/site/glist/templates/common/spellcheck_editor.html new file mode 100644 index 0000000..b203ebe --- /dev/null +++ b/site/glist/templates/common/spellcheck_editor.html @@ -0,0 +1,59 @@ + + + + diff --git a/site/glist/templates/common/spellcheck_inline.html b/site/glist/templates/common/spellcheck_inline.html new file mode 100644 index 0000000..475a480 --- /dev/null +++ b/site/glist/templates/common/spellcheck_inline.html @@ -0,0 +1,70 @@ + + +
    + <%hidden_objects%> + + + + + + + <%if emode eq 'multi'%><%endif%> +
    + diff --git a/site/glist/templates/common/spellcheck_objects.html b/site/glist/templates/common/spellcheck_objects.html new file mode 100644 index 0000000..927bba2 --- /dev/null +++ b/site/glist/templates/common/spellcheck_objects.html @@ -0,0 +1,7 @@ + + +<%if emode eq 'multi'%> + +<%endif%> +
    + diff --git a/site/glist/templates/gossamer/admin_initial_setup_first.html b/site/glist/templates/gossamer/admin_initial_setup_first.html new file mode 100644 index 0000000..d4a5dd2 --- /dev/null +++ b/site/glist/templates/gossamer/admin_initial_setup_first.html @@ -0,0 +1,78 @@ +<%GList::Config::tpl_load%> + + + + + Gossamer List Setup: Step 1 + <%include include_style.txt%> + + + +
    + + + + + + + + + <%hidden_objects%> + + + + +
    + + + + + + + +
    + + + + + + + +
    + + + + + +
    GList Setup - Step 1
    +
    + + + + + + + +
    +
    +

    Welcome to GList. Before you can begin to use the program, we need to setup GList to work with your + SQL server. You will need to know the following information before proceeding:

    +
    • SQL Server Type - common types include mysql, MS-SQL, Oracle
      +
    • SQL Hostname - which computer your sql server resides on, typically this is localhost.
      +
    • SQL Database Name - which database you want GList to use.
      +
    • SQL Username/Password - a username/password to log on to the SQL database. +
    +

    If you don't know the answer to any of this, please contact your ISP. If you are still stuck, please visit our support page at:

    +
    +

    http://gossamer-threads.com/scripts/support/

    +
    +
    +


    +
    Copyright 2002 Gossamer Threads Inc. 
    +
    +
    +
    + +
    + + diff --git a/site/glist/templates/gossamer/admin_initial_setup_second.html b/site/glist/templates/gossamer/admin_initial_setup_second.html new file mode 100644 index 0000000..884cdd7 --- /dev/null +++ b/site/glist/templates/gossamer/admin_initial_setup_second.html @@ -0,0 +1,114 @@ + + + + + Gossamer List Setup: Step 2 + <%include include_style.txt%> + + + +
    + + + + +
    + + + + + + + +
    + + + + + + + +
    + + + + + +
    GList Setup - Step 2
    +

    + + + + + + + +
    +
    + <%if error%> +

    Error:<%error%>

    +

    Please fix the error below and try again:

    + <%else%> + Please enter the following information: + <%endif%> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    SQL Server Type: + +
    SQL Hostname: + +
    SQL Database: + +
    SQL Username: + +
    SQL Password: + +
    Prefix:
    Overwrite + CHECKED<%endif%> /> +
    +

    GList will verify the program and create your SQL tables. If you have existing tables + and would like GList to drop and remove your data, click on Overwrite.

    +
    +

     


    +
    Copyright 2002 Gossamer Threads Inc. 
    +
    +
    +
    + + +<%hidden_objects%> +
    + + diff --git a/site/glist/templates/gossamer/admin_initial_setup_third.html b/site/glist/templates/gossamer/admin_initial_setup_third.html new file mode 100644 index 0000000..1ed4513 --- /dev/null +++ b/site/glist/templates/gossamer/admin_initial_setup_third.html @@ -0,0 +1,69 @@ + + + + + Gossamer List Setup: Step 3 + <%include include_style.txt%> + + + +
    + + + + +
    + + + + + + + +
    + + + + + + + +
    + + + + + +
    GList Setup - Step 3
    +

    + + + + + + + +
    +
    + <%if message%> +

    <%message%> + <%endif%> +


    + You can now proceed and use the + program, but are advised to look through the other setup options available by + clicking on setup above. If you had any problems, please hit back on your browser + and resubmit the form. +

    +
    +

    +
    +
    Copyright 2002 Gossamer Threads Inc. 
    +
    +
    +
    +<%if usr_email and !$use_cookie%> + +<%endif%> +
    + + diff --git a/site/glist/templates/gossamer/admin_initial_sql.html b/site/glist/templates/gossamer/admin_initial_sql.html new file mode 100644 index 0000000..b9db460 --- /dev/null +++ b/site/glist/templates/gossamer/admin_initial_sql.html @@ -0,0 +1,61 @@ + + + + + Gossamer List Setup SQL Database + <%include include_style.txt%> + + + +
    + + + + +
    + + + + + + + +
    + + + + + + + +
    + + + + + +
    GList Setup SQL Database
    +

    + + + + + + + +
    +
    + <%if msg%>
    <%msg%>
    <%endif%> + Please enter the following information: + <%include admin_setup_include_sql.html%> +
    +

     


    +
    Copyright 2002 Gossamer Threads Inc. 
    +
    +
    +
    + +<%hidden_objects%> +
    + + diff --git a/site/glist/templates/gossamer/admin_initial_sql_results.html b/site/glist/templates/gossamer/admin_initial_sql_results.html new file mode 100644 index 0000000..cd0e177 --- /dev/null +++ b/site/glist/templates/gossamer/admin_initial_sql_results.html @@ -0,0 +1,65 @@ + + + + + Gossamer List Setup SQL Database + <%include include_style.txt%> + + + + +
    + + + + +
    + + + + + + + +
    + + + + + + + +
    + + + + + +
    GList Setup SQL Database
    +

    + + + + + + + +
    +
    +
      <%msg%>
    +
    +

     


    +
    Copyright 2002 Gossamer Threads Inc. 
    +
    +
    +
    + +<%hidden_objects%> +
    + + diff --git a/site/glist/templates/gossamer/admin_setup_home.html b/site/glist/templates/gossamer/admin_setup_home.html new file mode 100644 index 0000000..5b06f45 --- /dev/null +++ b/site/glist/templates/gossamer/admin_setup_home.html @@ -0,0 +1,15 @@ +<%include include_header.html%> + + + + + + + + + +
    <%include admin_setup_include_menu.html%>
    +

      From here you can alter any of the options that GList uses, from your database settings to your email address. + Also, if you have made a mistake, with a simple click you can restore the system defaults.

    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/admin_setup_include_menu.html b/site/glist/templates/gossamer/admin_setup_include_menu.html new file mode 100644 index 0000000..bd88f40 --- /dev/null +++ b/site/glist/templates/gossamer/admin_setup_include_menu.html @@ -0,0 +1,34 @@ + + + + + + + + + + + + + +
    +   + SQL Server + + + Paths & URLs + + + Misc Options + + + User Signup + + + Stop List + + + Global Header & Footer + + +
    diff --git a/site/glist/templates/gossamer/admin_setup_include_sql.html b/site/glist/templates/gossamer/admin_setup_include_sql.html new file mode 100644 index 0000000..a8414ee --- /dev/null +++ b/site/glist/templates/gossamer/admin_setup_include_sql.html @@ -0,0 +1,68 @@ +
      +
    • Current Settings

      + + + + + + + + + + + + + + + + + + + + + + + + + +
      SQL Server Type + +
      SQL Hostname
      SQL Database
      SQL Username
      SQL Password
      Prefix
      +
    • +
    +
      +
    • + If you make changes here, Mailing Lists needs to examine the new database. Your choices are

      + + + + + + + + + + + + + +
      + + Create new default Mailing Lists tables in this database + but do not overwrite any existing data. +
      + + Create new default Mailing Lists tables in this database and overwrite/erase + any existing data. +
      + + Load table info from an existing set of tables (or resync the def + files if you have changed your defs manually). +

      +
    • +
    diff --git a/site/glist/templates/gossamer/admin_setup_mailing.html b/site/glist/templates/gossamer/admin_setup_mailing.html new file mode 100644 index 0000000..dbb6ad2 --- /dev/null +++ b/site/glist/templates/gossamer/admin_setup_mailing.html @@ -0,0 +1,63 @@ +<%set set_focus = 'myform.header_html'%> +<%set book_mark = 'misc_options'%> +<%include include_header.html%> + + + + + + + + + +
    <%include admin_setup_include_menu.html%>
    + + + + + + + +
    + + + + + +
    Misc Options

    + <%if msg%><%msg%><%else%>These are global header and footer that you can change:<%endif%>

    +
    + + + + + + + + + + + + + + + + + + + + + +
    HTML header
    header_html
    HTML footer
    footer_html
    Text header
    header_text
    Text footer
    footer_text
    +

    + + +

    + + + + <%hidden_objects%> +
    +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/admin_setup_misc.html b/site/glist/templates/gossamer/admin_setup_misc.html new file mode 100644 index 0000000..5f5f4ac --- /dev/null +++ b/site/glist/templates/gossamer/admin_setup_misc.html @@ -0,0 +1,150 @@ +<%set set_focus = 'myform.reg_number'%> +<%set book_mark = 'misc_options'%> +<%include include_header.html%> + + + + + + + + + +
    <%include admin_setup_include_menu.html%>
    + + + + + + + +
    + + + + + +
    Misc Options

    + <%if msg%><%msg%><%else%>These are other options that you can change:<%endif%>

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Template set to use
    template_set<%GList::Tools::_template_dir_select('template_set')%>
    Create backup templates (.bak)
    template_backups + +
    Registration Number
    reg_number50<%else%>30<%endif%> />
    Mail setup. If your server has sendmail, you should set mail_path to the path to sendmail. If you do not have sendmail (typically, Windows servers do not) or do not wish to use it, set smtp_server to the name of your SMTP server. Only one should be set!
    smtp_server50<%else%>30<%endif%> />
    mail_path50<%else%>30<%endif%> />
    Allowed size for attachments, in KB
    max_attachments_size50<%else%>30<%endif%> />
    The number bounced emails in POP account that you can check from the web.
    max_bounced_emails50<%else%>30<%endif%> />
    Mailing Lists Debug Level. This will display debug information into the error logs, and also show debug information on any fatal error.
    debug_level + +
    + Choose whether or not to generate a session_id. If this is set to "No", users will have to configure their browsers to allow cookies to view the database. +
    user_session + +
    Session expiry time, in hours
    session_exp
    Highlight Color
    highlight_color + +
    Display the limit bar
    display_limit_bar + +
    Use IFRAME for Open Message tracking
    iframe_tracking + +
    Custom Error Handler. Mailing Lists will display the following HTML on any error message that is generated. If left blank, the default error will be shown. You can only use two template tags in this: <%error%> for the error message, and <%environment%> for a complete debug message.
    error_message
    This should be HTML code, you can add the code below to your website to get new subscribers on the list.
    html_code
    +

    + + +

    + + + + <%hidden_objects%> +
    +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/admin_setup_path.html b/site/glist/templates/gossamer/admin_setup_path.html new file mode 100644 index 0000000..f313f97 --- /dev/null +++ b/site/glist/templates/gossamer/admin_setup_path.html @@ -0,0 +1,64 @@ +<%set set_focus = 'myform.priv_path'%> +<%set book_mark = 'path_options'%> +<%include include_header.html%> + + + + + + + + + +
    <%include admin_setup_include_menu.html%>
    +
    + + + + + + + +
    + + + + + +
    Paths & URLs

    + + + + +
    + <%if msg%><%msg%><%else%>These are the only paths you should need to set in the program. Most should be set properly and should not require changing.<%endif%>

    +
    + + + + + + + + + + + + + + + + + + +
    This should be the PATH to the directory where your library is. No trailing slash please.
    priv_path*
    This should be the URL to where your public cgi scripts are.
    cgi_url*
    This should be the URL to where your public images are.
    image_url*
    This should be the path to perl. NT Users should use drive letter as well.
    *: required
    +

    + + +

    +
    + + <%hidden_objects%> +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/admin_setup_signup.html b/site/glist/templates/gossamer/admin_setup_signup.html new file mode 100644 index 0000000..eb3ec41 --- /dev/null +++ b/site/glist/templates/gossamer/admin_setup_signup.html @@ -0,0 +1,92 @@ +<%set set_focus = 'myform.signup_restricted_email'%> +<%set book_mark = 'misc_options'%> +<%include include_header.html%> + + + + + + + + + +
    <%include admin_setup_include_menu.html%>
    + + + + + + + +
    + + + + + +
    User Signup Options

    + <%if msg%><%msg%><%else%>These are other options that you can change:<%endif%>

    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    If enabled, users can signup to the system.
    signup_enable + checked<%endif%>>Yes + checked<%endif%>>No +
    Enter email addresses that users will not be able to use to signup. Enter restricted addresses one per line.
    signup_restricted_email
    If enabled, users will be sent an e-mail containing a validation code when they sign up.
    signup_email_validate + checked<%endif%>>Yes + checked<%endif%>>No +
    If enabled, users must be validated by administrator user.
    signup_admin_validate + checked<%endif%>>Yes + checked<%endif%>>No +
    Enter a regex that will ensure that the usernames new users signup with are compatible with email. Note: If you are unfamiliar with regexes, do not alter this field.
    signup_username_regex50<%else%>30<%endif%> />
    Enter limit number of list.
    signup_limit_list
    Enter limit number of subscriber per list.
    signup_limit_sublist
    Enter limit number of email in the last 30 days.
    signup_limit_email30
    +

    + + +

    + + + + <%hidden_objects%> +
    +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/admin_setup_sql_form.html b/site/glist/templates/gossamer/admin_setup_sql_form.html new file mode 100644 index 0000000..e4603da --- /dev/null +++ b/site/glist/templates/gossamer/admin_setup_sql_form.html @@ -0,0 +1,48 @@ +<%set set_focus = 'myform.sql_driver'%> +<%include include_header.html%> + + + + + + + + + +
    <%include admin_setup_include_menu.html%>
    + + + + + + + +
    + + + + + +
    SQL Server Settings

    +
    + + + + + + + +
    + <%if msg%>
      <%msg%>
    <%else%>

    All data in GList stored in an SQL database. This screen allows you to change which SQL server to use:


    <%endif%> +
    + <%include admin_setup_include_sql.html%> +

    + +

    +
    + + <%hidden_objects%> +
    +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/admin_stoplist.html b/site/glist/templates/gossamer/admin_stoplist.html new file mode 100644 index 0000000..8f951ef --- /dev/null +++ b/site/glist/templates/gossamer/admin_stoplist.html @@ -0,0 +1,102 @@ + + + <%include include_header.html%> + <%set book_mark = 'stop_list'%> + + + + + + + + + +
    <%include admin_setup_include_menu.html%>
    + + + + + + + +
    + + + + + +
    Stop List

    +
    + + + + + + + + + +
    Search + + +
    Quick Search<%search_bar%>
    + + <%hidden_objects%> +
    + + <%if msg%><%endif%> + <%if hits > $mh and $mh != -1%> + + + + <%endif%> +
    <%msg%>
    <%include include_toolbar.html%>
    +
    + + + + +
    + + + + + + <%loop results%> + + + + + + <%endloop%> +
    + <%ifnot hits%>Email + <%else%> + Email + <%ifnot sb%> + + <%endif%> + <%endif%> +
    <%stl_email%>
    +
    + + + <%if hits > $mh and $mh != -1%> + + + + <%endif%> + + +
    <%include include_toolbar.html%>
    + + <%if hits%><%endif%> +
    + + <%hidden_query%> +
    +
    +
    +<%include include_footer.html%> + + diff --git a/site/glist/templates/gossamer/admin_stoplist_form.html b/site/glist/templates/gossamer/admin_stoplist_form.html new file mode 100644 index 0000000..67d8621 --- /dev/null +++ b/site/glist/templates/gossamer/admin_stoplist_form.html @@ -0,0 +1,81 @@ + + + <%include include_header.html%> + <%set book_mark = 'stop_list'%> + + + + + + + + + +
    <%include admin_setup_include_menu.html%>
    + + + + + + + +
    + + + + + +
    Add Stop List

    +
    + + <%if msg%><%endif%> + <%if confirmation%> + + + + + + + <%else%> + + + + + + + <%endif%> +
    <%msg%>

    + The new Stop List will permanently remove <%found_emails%> addresses from active lists.

    +
    + + + + + + <%loop loop_results%> + + + + + <%endloop%> +
    EmailDelete
    <%email%><%if found%><%found%><%endif%>
    +
    +
    + Are you sure you want to add above email(s) to your Stop List?
    + + + +
    Email Addresses
    + +

    + + +
    + + <%hidden_objects%> +
    +
    +
    +<%include include_footer.html%> + + diff --git a/site/glist/templates/gossamer/admin_stoplist_success.html b/site/glist/templates/gossamer/admin_stoplist_success.html new file mode 100644 index 0000000..f2c40cd --- /dev/null +++ b/site/glist/templates/gossamer/admin_stoplist_success.html @@ -0,0 +1,50 @@ +<%set book_mark = 'stop_list'%> +<%include include_header.html%> + + + + + + + + + +
    <%include admin_setup_include_menu.html%>
    + + + + + + + +
    + + + + + +
    Stop List
    +
    +
    + + + + + <%loop results%> + + + + + <%endloop%> +

    <%successful%> out of <%hits%> email addresses were added. + <%if invalid%>
    <%invalid%> invalid email addresses.<%endif%> + <%if duplicate%>
    <%duplicate%> duplicate email addresses.<%endif%> +

    +
    <%email%>  [ <%if status%><%status%><%else%>Added<%endif%> ]
    +
    +
    +
    +
    +<%include include_footer.html%> + + diff --git a/site/glist/templates/gossamer/admin_template_default.html b/site/glist/templates/gossamer/admin_template_default.html new file mode 100644 index 0000000..cef5dcd --- /dev/null +++ b/site/glist/templates/gossamer/admin_template_default.html @@ -0,0 +1,148 @@ +<%set set_focus = 'myform.tpl_dir'%> +<%set book_mark = 'tpl_email'%> +<%include include_header.html%> + + + + + + + + + +
    <%include admin_template_include_menu.html%>
    + + + + + + + +
    + + + + + +
    Edit Templates
    +
    + + + + +

    + <%GList::Tools::default_email_editor%> + <%if bsave%> + <%if save_as%> + <%set template_file = $save_as%> + <%else%> + <%set template_file = $tpl_file%> + <%endif%> + <%if demo%> + <%set msg = 'Template Editor has been disabled in the demo!'%> + <%else%> + <%if tpl_to eq '' or tpl_from eq '' or tpl_subject eq '' or tpl_body eq ''%> + <%set error = 'Required fields are not specified!'%> + <%else%> + <%GT::Mail::Editor::tpl_save( + dir => $tpl_dir, + template => $selected_dir, + file => $template_file, + header => To => $tpl_to, + header => Subject => $tpl_subject, + header => From => $tpl_from, + extra_headers => $extra_headers, + body => $tpl_body + )%> + <%endif%> + <%if error%> + <%set msg = $error%> + <%set color = red%> + <%else%> + <%set msg = "Template saved successfully."%> + <%endif%> + <%endif%> + <%elsif bload%> + <%GT::Mail::Editor::tpl_load( + dir => $tpl_dir, + template => $selected_dir, + file => $tpl_file, + header => To, + header => From, + header => Subject + )%> + <%if error%> + <%set msg = $error%> + <%set color = red%> + <%else%> + <%set loaded = 1%> + <%endif%> + <%endif%> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Templates Set + <%dir_select%> + +
    Templates + <%select_list%> + +
    To + * +
    Subject + * +
    From + * +
    Extra Headers + +
    E-mail body * + *: required +
    + +
    + <%if tpl_file%> + + + <%endif%> + <%if msg%><%msg%><%endif%> +
    +
    + + + <%hidden_objects%> +
    +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/admin_template_global.html b/site/glist/templates/gossamer/admin_template_global.html new file mode 100644 index 0000000..7e04ee5 --- /dev/null +++ b/site/glist/templates/gossamer/admin_template_global.html @@ -0,0 +1,62 @@ +<%set book_mark = 'tpl_global'%> +<%GList::Tools::global_editor%> +<%include include_header.html%> + + + + + + + + + +
    <%include admin_template_include_menu.html%>
    + + + + + + + +
    + + + + + +
    Globals

    + <%if message%><%message%><%else%>From here you can quickly edit globals that can be used in any template.<%endif%>

    +
    + + + + +
    + + + + + + + <%global_table%> + + + + + +
    Global NameValueDelete/
    Restore
    + New: + + + + +  
    +

    +
    + + + <%hidden_objects%> +
    +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/admin_template_home.html b/site/glist/templates/gossamer/admin_template_home.html new file mode 100644 index 0000000..3a5d6cd --- /dev/null +++ b/site/glist/templates/gossamer/admin_template_home.html @@ -0,0 +1,36 @@ +<%set set_focus = 'myform.tpl_dir'%> +<%set book_mark = 'template'%> +<%include include_header.html%> + + + + + + + + + +
    <%include admin_template_include_menu.html%>
    + + + + + + + +
    +
    + + + + + +
    Edit Templates
    + <%include admin_template_include_record.html%> + + + <%hidden_objects%> +
    +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/admin_template_include_menu.html b/site/glist/templates/gossamer/admin_template_include_menu.html new file mode 100644 index 0000000..7bc639b --- /dev/null +++ b/site/glist/templates/gossamer/admin_template_include_menu.html @@ -0,0 +1,26 @@ + + + + + + + + + + + +
    +   + Templates + + + Default Email Templates + + + Language + + + Template Globals + + +
    diff --git a/site/glist/templates/gossamer/admin_template_include_record.html b/site/glist/templates/gossamer/admin_template_include_record.html new file mode 100644 index 0000000..726685f --- /dev/null +++ b/site/glist/templates/gossamer/admin_template_include_record.html @@ -0,0 +1,165 @@ + + +<%GList::Tools::template_editor%> +<%if readme%>

    <%readme%>

    <%endif%> + + + + + + + + + + + + + + <%if tpl_name%> + + + + + <%endif%> + + + + + + + + + + +

    + <%if success_message%><%success_message%><%elsif error_message%><%error_message%><%else%>From here you can quickly edit any of your user or admin templates.<%endif%>

    +
    Template Set + <%dir_select%> + +
    Available templates<%file_select%> +
    + (* denotes a modified or non-standard template) +
    Currently working on + <%tpl_name%> + <%if file_local%> + <%if file_restore%>  (diff)  <%endif%> + value="Restore" name="restore=<%tpl_name%>"<%else%>value="Delete" name="delete=<%tpl_name%>"<%endif%> /> + <%endif%> +
    Save template as + + +
    + <%if is_ie or is_mozilla%> +

    + <%else%> +

    + <%endif%> +
    + + + + + +
    + <%if is_ie or is_mozilla%> + <%if is_ie%> + + <%endif%> + + rows + cols + <%else%> + + rows + cols + <%endif%> + + <%if is_ie%> + + <%endif%> +
    +
    +
    + diff --git a/site/glist/templates/gossamer/admin_template_language.html b/site/glist/templates/gossamer/admin_template_language.html new file mode 100644 index 0000000..a114bb7 --- /dev/null +++ b/site/glist/templates/gossamer/admin_template_language.html @@ -0,0 +1,74 @@ +<%set set_focus = 'myform.tpl_dir'%> +<%set book_mark = 'tpl_language'%> +<%GList::Tools::language_editor%> +<%include include_header.html%> + + + + + + + + + +
    <%include admin_template_include_menu.html%>
    +
    + + + + + + + +
    + + + + + +
    Language
    +
    + <%if message%><%message%><%else%>From here you can quickly edit the language settings of your templates.<%endif%>

    + + + + + + + +
    Template Set: + <%dir_select%> + +
    +
    <%prefix_list%>
    + + + + +
    + <%if prefix%> + + + + + + + <%language_table%> + + + + + +
    Language CodeValueDelete/
    Restore
    New:  
    +

    + <%endif%> +
    +
    + + + + <%hidden_objects%> +
    +
    +<%include include_footer.html%> + diff --git a/site/glist/templates/gossamer/admin_user_add_form.html b/site/glist/templates/gossamer/admin_user_add_form.html new file mode 100644 index 0000000..6971b0a --- /dev/null +++ b/site/glist/templates/gossamer/admin_user_add_form.html @@ -0,0 +1,49 @@ +<%set set_focus = 'myform.mod_usr_username'%> +<%set book_mark = 'add_user'%> +<%include include_header.html%> + + + + + + + + + +
    <%include admin_user_include_menu.html%>
    + + + + + + + +
    + + + + + +
    Add a New User
    + + + + + + + + + + <%hidden_objects%> + +

    + <%if msg%>

    <%msg%>

    <%else%><%endif%> + <%include admin_user_include_record.html%> +
    + +

    +
    +
    +
    + +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/admin_user_home.html b/site/glist/templates/gossamer/admin_user_home.html new file mode 100644 index 0000000..1f38f94 --- /dev/null +++ b/site/glist/templates/gossamer/admin_user_home.html @@ -0,0 +1,99 @@ + +<%set book_mark = 'user_management'%> +<%include include_header.html%> + + + + + + + + + +
    <%include admin_user_include_menu.html%>
    +
    + + + + + + + +
    + <%set log_usr_type = $usr_type%> + + + + + + + + + <%set usr_current = $usr_username%> + <%loop results%> + + + + + + + + <%endloop%> +
    + Type + <%if sb eq 'usr_type' or sb eq ''%> + + <%endif%> + + Username + <%if sb eq 'usr_username'%> + + <%endif%> + + Email + <%if sb eq 'usr_email'%> + + <%endif%> + + Full Name + <%if sb eq 'pro_first_name'%> + + <%endif%> +
    + <%if usr_type == 1%>Administrator<%elsif usr_type == 2%>Limited User<%elsif usr_type == 3%>Unlimited User<%else%>Unvalidated User<%endif%> + + <%usr_username%> + + <%usr_email%> + <%pro_first_name%> <%pro_last_name%> + <%if usr_current ne $usr_username%> + + + <%endif%> +
    + + <%if hits > $mh%> + + + + <%endif%> + + + + +
    <%include include_toolbar.html%>
    <%if msg%><%msg%><%endif%> + + <%if hits%> + + <%if signup_enable%> + + <%endif%> + + <%endif%> +
    +
    + + <%hidden_query%> +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/admin_user_include_menu.html b/site/glist/templates/gossamer/admin_user_include_menu.html new file mode 100644 index 0000000..f598cb2 --- /dev/null +++ b/site/glist/templates/gossamer/admin_user_include_menu.html @@ -0,0 +1,10 @@ + + + + + + + + + +
     User ManagementCustomize Profile
    diff --git a/site/glist/templates/gossamer/admin_user_include_record.html b/site/glist/templates/gossamer/admin_user_include_record.html new file mode 100644 index 0000000..bfb1410 --- /dev/null +++ b/site/glist/templates/gossamer/admin_user_include_record.html @@ -0,0 +1,199 @@ + + + + + + + + + + <%if modify%> + + + + + <%else%> + + + + + <%endif%> + + + + + + + + + + + + + + + + + <%if usr_type == 1%> + + + + + <%endif%> + <%if mod_usr_username ne $usr_username%> + + + + + + + + + + + + + + + + <%endif%> + + + + <%-- + This auto generates the profile form, so if you add profile options it + will automatically appear. + --%> + <%GList::GUI::gui_profile_form( + td_l => 'class="body" align="left"', + td_r => 'class="body" align="left"', + required => '*', + prefix => 'mod_' + )%> + <%-- + Now add any extra application fields needed. + --%> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  • Account Infomation
    • Username
    + <%if modify%><%mod_usr_username%> + + + <%else%> + * + <%endif%> +
      New Password
    + +
      Password
    + * +
      Email Address
    + * +
      Reply-to Email
    + +
      Bounce Email
    + +
      Type
    + <%if mod_usr_username eq $usr_username%> + <%if usr_type == 1%>Administrator<%elsif usr_type == 2%>Limited User<%elsif usr_type == 3%>Unlimited User<%else%>Unvalidated User<%endif%> + <%else%> + + <%endif%> +
      Status
    + +
  • Account Limits
    • Number of list
    + +
      Number of subscriber per list
    + +
      Number of email in the last 30 days
    + +
  • Profile Infomation
  • Default Values
    • Compose Mode
    + +
      Advanced Editor
    + +
      Date format
    +
    + +

      The pop settings below are for bounced emails.
      Server mail (POP3)
    + +
      Port
    + +
      Account Name
    + 35<%else%>25<%endif%> value="<%if mod_usr_mail_account%><%mod_usr_mail_account%><%endif%>" /> +
      Account Password
    + 35<%else%>25<%endif%> value="<%if mod_usr_mail_password%><%mod_usr_mail_password%><%endif%>" /> +
    (*): Required
    diff --git a/site/glist/templates/gossamer/admin_user_modify_form.html b/site/glist/templates/gossamer/admin_user_modify_form.html new file mode 100644 index 0000000..2589500 --- /dev/null +++ b/site/glist/templates/gossamer/admin_user_modify_form.html @@ -0,0 +1,57 @@ +<%set set_focus = 'myform.mod_pro_first_name'%> +<%set book_mark = 'modify_user'%> +<%include include_header.html%> + + + + + + + + + +
    <%include admin_user_include_menu.html%>
    + + + + + + + +
    + + + + + +
    Modify User
    +
    + + + + + <%if usr_type == 3 and $mod_usr_validate_code%> + + + + <%endif%> + + + +

    + <%if msg%>

    <%msg%>

    <%endif%> + <%include admin_user_include_record.html%> +
    +
      Do you want to email validate this user? + Yes + No

      +
    + +

    +
    + + <%hidden_objects%> +
    +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/admin_user_search_form.html b/site/glist/templates/gossamer/admin_user_search_form.html new file mode 100644 index 0000000..b21801f --- /dev/null +++ b/site/glist/templates/gossamer/admin_user_search_form.html @@ -0,0 +1,116 @@ +<%set set_focus = 'myform.usr_username'%> +<%set book_mark = 'search_user'%> +<%include include_header.html%> + + + + + + + + + +
    <%include admin_user_include_menu.html%>
    + + + + + + + +
    + + + + + +
    Search User
    +
    + + + + +
    + <%if msg%>


    <%msg%>

    <%endif%>
    + + + + + + + + + + + + + + + + + + + + + + + + + <%GList::GUI::gui_profile_form( + td_l => 'class="body" align="left"', + td_r => 'class="body" colspan="2" align="left"', + mode => 'search', + required => '' + )%> + + + + + + + + + + + + + + + +
  • Search Info
    • Type
    + +
      Username
    + + +
      Email
    + +
      Reply-to Email
      Bounce Email
  • Search Options
    • Maximum hits
    Match Any:
      Sort by
    + + +

    + + +

    +
    + + <%hidden_objects%> +
    +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/admin_user_table.html b/site/glist/templates/gossamer/admin_user_table.html new file mode 100644 index 0000000..e5c913f --- /dev/null +++ b/site/glist/templates/gossamer/admin_user_table.html @@ -0,0 +1,57 @@ + +<%set book_mark = 'user_management'%> +<%include include_header.html%> + + + + + + + + + +
    <%include admin_user_include_menu.html%>
    +
    + + + + + + + +
    + + + + + + + + + <%loop loop_fields%> + + + + + + + + <%endloop%> +
    Column NameColumn TypeNot NullDefaultRegex
    <%name%><%type%><%if size%> (<%size%>)<%endif%><%if not_null%>Yes<%else%>No<%endif%><%if default%><%default%><%else%> <%endif%><%if regex%><%regex%><%else%> <%endif%>
    + + + + + + + +
    + + +
    <%if msg%><%msg%><%else%>Click on a "Column Name" to modify or delete an existing column, or click the "Add A Field" button.<%endif%>
    +
    + + <%hidden_objects%> +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/admin_user_table_add.html b/site/glist/templates/gossamer/admin_user_table_add.html new file mode 100644 index 0000000..3b698e2 --- /dev/null +++ b/site/glist/templates/gossamer/admin_user_table_add.html @@ -0,0 +1,49 @@ +<%set set_focus = 'myform.mod_usr_username'%> +<%set book_mark = 'add_user'%> +<%include include_header.html%> + + + + + + + + + +
    <%include admin_user_include_menu.html%>
    + + + + + + + +
    + + + + + +
    Add a Field
    + + + + + + + + + + <%hidden_objects%> + +

    + <%if msg%><%msg%><%else%>Add a profile field<%endif%>

    + <%include admin_user_table_include_record.html%>
    +
    + +

    +
    +
    +
    + +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/admin_user_table_include_record.html b/site/glist/templates/gossamer/admin_user_table_include_record.html new file mode 100644 index 0000000..794f30a --- /dev/null +++ b/site/glist/templates/gossamer/admin_user_table_include_record.html @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
  • Database Infomation
    • Column Name
    + <%if column%> + <%column%> + + <%else%> + + <%endif%> +
      Column Type
    + +
      Column Index
    + +
      Column Size
      (Only for CHAR types)
    + +
      Not Null
    + checked<%endif%>>Yes + checked<%endif%>>No +
      Default
    + +
  • Form Infomation
    • Form Display
    + +
      Form Type
    + +
      Form Size
    + +
      Form Names:
      (Stored in Database)
      Only for checkbox, multi-select or radio forms.
    + +
      Form Values:
      (Stored in Form)
      Only for checkbox, multi-select or radio forms.
    + +
      Form Regex
    + +
    diff --git a/site/glist/templates/gossamer/admin_user_table_modify.html b/site/glist/templates/gossamer/admin_user_table_modify.html new file mode 100644 index 0000000..b335720 --- /dev/null +++ b/site/glist/templates/gossamer/admin_user_table_modify.html @@ -0,0 +1,66 @@ +<%set set_focus = 'myform.mod_usr_username'%> +<%set book_mark = 'add_user'%> +<%include include_header.html%> + + + + + + + + + + +
    <%include admin_user_include_menu.html%>
    + + + + + + + +
    + + + + + +
    Modify a Field
    + + + + + + + + + + <%hidden_objects%> + +

    + <%if msg%><%msg%><%else%>Modify the profile field and click the "Modify" button to save your changes. Click "Delete" if you wish to permanently remove this column from your database<%endif%>

    + <%include admin_user_table_include_record.html%>
    +
    + + <%if column ne 'pro_first_name' and $column ne 'pro_last_name'%> + + <%endif%> +

    +
    +
    +
    + +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/error_form.html b/site/glist/templates/gossamer/error_form.html new file mode 100644 index 0000000..4bbe495 --- /dev/null +++ b/site/glist/templates/gossamer/error_form.html @@ -0,0 +1,45 @@ + + + <%if site_title%><%site_title%><%else%>Gossamer Gossamer List<%endif%> - Login + <%include include_style.txt%> + + + + + + + + + +
    + + + + + + + +
    + + + + + + +
    <%if header%><%header%><%else%>Error<%endif%>
    +
    + + + + + + + +
    <%msg%>
    + +
    +
    +
    +
    + + diff --git a/site/glist/templates/gossamer/gt_doc.html b/site/glist/templates/gossamer/gt_doc.html new file mode 100644 index 0000000..cfcb1b6 --- /dev/null +++ b/site/glist/templates/gossamer/gt_doc.html @@ -0,0 +1,35 @@ +<%include include_header.html%> + + + + + + + + + +
    <%include plugin_include_menu.html%>
    + + + + + + + +
    + + + + + +
    GT Module Documentation

    + + + + +
    + +
    +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/include_footer.html b/site/glist/templates/gossamer/include_footer.html new file mode 100644 index 0000000..2ca2e18 --- /dev/null +++ b/site/glist/templates/gossamer/include_footer.html @@ -0,0 +1,43 @@ + + + <%if selected_menu == 1 or selected_menu == 3 or selected_menu eq ''%> + + + + + + <%else%> + + + + <%endif%> + + + + + + + +
    + <%if usr_type == 2 and $display_limit_bar%> + <%include include_used_space.html%> + <%else%> + + <%endif%> + + <%if usr_type == 2 and $display_limit_bar%> + <%set view_subscriber = 1%> + <%include include_used_space.html%> + <%else%> + + <%endif%> +
    + + + + + +
    <%if site_title%><%site_title%><%endif%> version <%if version%><%version%><%endif%>Copyright ©2003 Gossamer Threads Inc.
    +
    + + diff --git a/site/glist/templates/gossamer/include_header.html b/site/glist/templates/gossamer/include_header.html new file mode 100644 index 0000000..02d55f4 --- /dev/null +++ b/site/glist/templates/gossamer/include_header.html @@ -0,0 +1,22 @@ + + +<%if site_title%><%site_title%><%else%>Gossamer List<%endif%> +<%include include_style.txt%> + + + + +<%if usr_type == 1%> + <%include include_header_admin.html%> +<%else%> + <%include include_header_user.html%> +<%endif%> diff --git a/site/glist/templates/gossamer/include_header_admin.html b/site/glist/templates/gossamer/include_header_admin.html new file mode 100644 index 0000000..2228e24 --- /dev/null +++ b/site/glist/templates/gossamer/include_header_admin.html @@ -0,0 +1,285 @@ + + + + + + + + + + + + + + + + + + + + + + + + <%if selected_menu == 9%> + + + <%else%> + + <%endif%> + +
    + + + + + + + +
    + +
    +
    + <%if selected_menu == 1 or selected_menu eq ''%> + + + + + + + + + +
    +
    +
    +
    Messages
    +
    + <%else%> + + + + + + +
    + +
    + <%endif%> +
    + <%if selected_menu == 2 %> + + + + + + + + + +
    +
    +
    +
    Lists
    +
    + <%else%> + + + + + + +
    + +
    + <%endif%> +
    + <%if selected_menu == 3 %> + + + + + + + + + +
    +
    +
    +
    Mailings
    +
    + <%else%> + + + + + + +
    + +
    + <%endif%> +
    + <%if selected_menu == 4 %> + + + + + + + + + +
    +
    +
    +
    Reports
    +
    + <%else%> + + + + + + +
    + +
    + <%endif%> +
    + <%if selected_menu == 5 %> + + + + + + + + + +
    +
    +
    +
    Account
    +
    + <%else%> + + + + + + +
    + +
    + <%endif%> +
    + <%if selected_menu == 6 %> + + + + + + + + + +
    +
    +
    +
    Users
    +
    + <%else%> + + + + + + +
    + +
    + <%endif%> +
    + <%if selected_menu == 7 %> + + + + + + + + + +
    +
    +
    +
    Templates
    +
    + <%else%> + + + + + + +
    + +
    + <%endif%> +
    + <%if selected_menu == 8 %> + + + + + + + + + +
    +
    +
    +
    Plugins
    +
    + <%else%> + + + + + + +
    + +
    + <%endif%> +
    colspan=2<%endif%>> + <%if selected_menu == 9 %> + + + + + + + + + +
    +
    +
    +
    Setup
    +
    + <%else%> + + + + + + +
    + +
    + <%endif%> +
    diff --git a/site/glist/templates/gossamer/include_header_user.html b/site/glist/templates/gossamer/include_header_user.html new file mode 100644 index 0000000..979cc2f --- /dev/null +++ b/site/glist/templates/gossamer/include_header_user.html @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + <%if selected_menu == 5%> + + + <%else%> + + <%endif%> + +
    + + + + + + + +
    + +
    +
    + <%if selected_menu == 1 or selected_menu eq ''%> + + + + + + + + + +
    +
    +
    +
    Messages
    +
    + <%else%> + + + + + + +
    + +
    + <%endif%> +
    + <%if selected_menu == 2 %> + + + + + + + + + +
    +
    +
    +
    Lists
    +
    + <%else%> + + + + + + +
    + +
    + <%endif%> +
    + <%if selected_menu == 3 %> + + + + + + + + + +
    +
    +
    +
    Mailings
    +
    + <%else%> + + + + + + +
    + +
    + <%endif%> +
    + <%if selected_menu == 4 %> + + + + + + + + + +
    +
    +
    +
    Reports
    +
    + <%else%> + + + + + + +
    + +
    + <%endif%> +
    colspan=2<%endif%>> + <%if selected_menu == 5 %> + + + + + + + + + +
    +
    +
    +
    Account
    +
    + <%else%> + + + + + + +
    + +
    + <%endif%> +
    diff --git a/site/glist/templates/gossamer/include_toolbar.html b/site/glist/templates/gossamer/include_toolbar.html new file mode 100644 index 0000000..c1ce655 --- /dev/null +++ b/site/glist/templates/gossamer/include_toolbar.html @@ -0,0 +1,21 @@ +Page:  +<%set num_page_items = $hits%> +<%GList::GUI::gui_toolbar( + first => 'First page', + first_grey => 'First page', + prev => 'Previous page', + prev_grey => 'Previous page', + next => 'Next page', + next_grey => 'Next page', + last => 'Last page', + last_grey => 'Last page', + view_all => 'View All', + pages => 9, + '...' => '...', + 'first_...' => 1, + 'last_...' => 1, + before_page => '', + after_page => '', + before_current => '', + after_current => '', +)%> diff --git a/site/glist/templates/gossamer/include_used_space.html b/site/glist/templates/gossamer/include_used_space.html new file mode 100644 index 0000000..882e790 --- /dev/null +++ b/site/glist/templates/gossamer/include_used_space.html @@ -0,0 +1,31 @@ + <%if view_subscriber%> + <%GList::Tools::generate_used_bar('sublist', 220)%> + <%else%> + <%GList::Tools::generate_used_bar('email30', 220)%> + <%endif%> +
    + + + <%if used_percent%> + + <%endif%> + + +
    + <%used_message%> + + + + +
    + + + + + +
    +
    +
    +
    + +
    diff --git a/site/glist/templates/gossamer/local/lst_sub_include_record.html b/site/glist/templates/gossamer/local/lst_sub_include_record.html new file mode 100644 index 0000000..9ca2cc6 --- /dev/null +++ b/site/glist/templates/gossamer/local/lst_sub_include_record.html @@ -0,0 +1,129 @@ + + <%if do eq 'lst_sub_modify'%> + + + + + + + + + + + + + + <%else%> + + + + + <%endif%> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    List<%lst_title%>
    Validated + +
    Bounced + +
    List + * +
    Email address + * +
    Name + +
    Company Name + +
    Website Address:
    Shipping Address:
    Race Director Name:
    Race Director Phone:
    Race Director Email:
    Sponsorship coordinator Name:
    Sponsorship coordinator Phone:
    Sponsorship coordinator Email:
    Press Relation Manager Name:
    Press Relation Manager Phone:
    Press Relation Manager Email:
    Pro Athlete Liaison Name:
    Pro Athlete Liaison Phone:
    Pro Athlete Liaison Email:
    Goods & Services Buyer Name:
    Goods & Services Buyer Phone:
    Goods & Services Buyer Email:
    *: required
    diff --git a/site/glist/templates/gossamer/local/lst_subscribers.html b/site/glist/templates/gossamer/local/lst_subscribers.html new file mode 100644 index 0000000..5b60638 --- /dev/null +++ b/site/glist/templates/gossamer/local/lst_subscribers.html @@ -0,0 +1,179 @@ + + + <%include include_header.html%> + + + + + + + + + +
    <%include lst_include_menu.html%>
    + + + + + + + +
    + + + + + +
    Subscribers + <%set mn_subscriber = 1%> + <%set isub_add = 1%> + <%set isub_import = 1%> + <%set iremove_sub = 1%> + <%include lst_include_icons.html%> +

    +
    + + + + + + + + + +
    Search + + By  + + in  + + + [ Advanced ] +
    Quick Search<%search_bar%>
    + + <%hidden_objects%> +
    + <%if msg%>

    <%msg%>

    <%endif%> +
    + <%if lst_user_id_fk and lst_user_id_fk ne $usr_username%> + <%set user_search = 1%> + <%elsif lst_user_id_fk eq $usr_username and lst_user_id_fk-opt eq '<>'%> + <%set user_search = 1%> + <%endif%> + <%if hits > $mh and $mh != -1%> + + + + +
    <%include include_toolbar.html%>
    + <%endif%> + + + + +
    + + + + + + + + + + + <%loop results%> + + + + + + + + + + + <%endloop%> +
    + <%ifnot hits%>Email + <%else%> + Email + <%ifnot sb%> + + <%endif%> + <%endif%> + + <%ifnot hits%>Name + <%else%> + Name + <%if sb eq 'sub_name'%> + + <%endif%> + <%endif%> + + <%ifnot hits%>Company Name + <%else%> + Company Name + <%if sb eq 'sub_company'%> + + <%endif%> + <%endif%> + + <%ifnot hits%>List + <%else%> + List + <%if sb eq 'sub_list_id_fk'%> + + <%endif%> + <%endif%> + + <%ifnot hits%>Validated + <%else%> + Validated + <%if sb eq 'sub_validated'%> + + <%endif%> + <%endif%> + + <%ifnot hits%>Bounced + <%else%> + Bounced + <%if sb eq 'sub_bounced'%> + + <%endif%> + <%endif%> +
    <%sub_email%><%if sub_name%><%sub_name%><%else%> <%endif%><%if sub_company%><%sub_company%><%else%> <%endif%><%GList::Tools::list_title($sub_list_id_fk)%><%if sub_validated%>Yes<%else%>No<%endif%><%if sub_bounced%><%sub_bounced%><%else%>0<%endif%>
    +
    + <%if hits%> + + <%if hits > $mh and $mh != -1%> + + + + <%endif%> + + + +
    <%include include_toolbar.html%>
    + + + +
    + <%endif%> + + + + <%hidden_objects%> +
    +
    +
    +<%include include_footer.html%> + + diff --git a/site/glist/templates/gossamer/lst_add_form.html b/site/glist/templates/gossamer/lst_add_form.html new file mode 100644 index 0000000..1568185 --- /dev/null +++ b/site/glist/templates/gossamer/lst_add_form.html @@ -0,0 +1,49 @@ +<%set set_focus = 'myform.lst_title'%> +<%set book_mark = 'create_list'%> +<%include include_header.html%> + + + + + + + + + +
    <%include lst_include_menu.html%>
    + + + + + + + +
    + + + + + +
    Create List
    +
    + + + + + + + +
    + <%if msg%>


      <%msg%>

    <%else%>
    <%endif%> + <%include lst_include_record.html%> +
    + + + +
    + + <%hidden_objects%> +
    +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/lst_home.html b/site/glist/templates/gossamer/lst_home.html new file mode 100644 index 0000000..b04d2b4 --- /dev/null +++ b/site/glist/templates/gossamer/lst_home.html @@ -0,0 +1,30 @@ + + +<%include include_header.html%> + + + + + + + + + +
    <%include lst_include_menu.html%>
    +
    + <%include lst_include_results.html%> + + <%hidden_objects%> +
    +
    +<%include include_footer.html%> + diff --git a/site/glist/templates/gossamer/lst_html.html b/site/glist/templates/gossamer/lst_html.html new file mode 100644 index 0000000..1d39d52 --- /dev/null +++ b/site/glist/templates/gossamer/lst_html.html @@ -0,0 +1,49 @@ +<%include include_header.html%> +<%set book_mark = 'generate_html'%> + + + + + + + + + +
    <%include lst_include_menu.html%>
    + + + + + + + +
    + + + + + +
    HTML of a List + <%set isub_import = 1%> + <%set imodify = 1%> + <%set isearch_sub = 1%> + <%set iremove_sub = 1%> + <%set iclose = 1%> + <%include lst_include_icons.html%> +
    + + + + + + + +
    + To make it easy for people to subscribe to this list, you can add the code below to your website to get new subscribers!

    +
    + +

    +
    +
    +<%include include_footer.html%> + diff --git a/site/glist/templates/gossamer/lst_import_form.html b/site/glist/templates/gossamer/lst_import_form.html new file mode 100644 index 0000000..39ecec2 --- /dev/null +++ b/site/glist/templates/gossamer/lst_import_form.html @@ -0,0 +1,132 @@ +<%set set_focus = 'myform.import_to'%> +<%set book_mark = 'import_subscriber'%> +<%include include_header.html%> + + + + + + + + + +
    <%include lst_include_menu.html%>
    + + + + + + + +
    + + + + + +
    Import Subscribers + <%set mn_subscriber = 1%> + <%set isub_search = 1%> + <%set isub_add = 1%> + <%set iremove_sub = 1%> + <%set iclose = 1%> + <%include lst_include_icons.html%> +
    +
    +
    +

    <%if msg%><%msg%><%else%>This lets you to import the subscribers from either an existing text file or a list.<%endif%>

    +
    +
    + + + + + + + + + + + + + + + + + + + + + + +
  • Import into
    Select lists from the list below
  • + +
  • Import Data
  • + + + + + + + + + + + + + +
    From file + +
    OR  
    Subscribers + +
    +
  • Fields To Import
  • + + + + + + + +
    + + Subscriber Name +
    + +
    +
    Subscriber Email

    +
  • Import Options
  • + + + + + + + + + + + +
    FieldsDelimiter
    +
    +
    Escape charactor
    + +
    RecordsDelimiter
    + +
    +
    Ignore first
    + lines
    +
    + + +
    + + + <%hidden_objects%> +
    +
    +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/lst_import_success.html b/site/glist/templates/gossamer/lst_import_success.html new file mode 100644 index 0000000..664d062 --- /dev/null +++ b/site/glist/templates/gossamer/lst_import_success.html @@ -0,0 +1,85 @@ +<%include include_header.html%> + + + + + + + + + +
    <%include lst_include_menu.html%>
    + + + + + + + +
    + + + + + + + +
    + + + + +
    + + + + + +
    Import Subscribers + <%set mn_subscriber = 1%> + <%set isub_search = 1%> + <%set isub_add = 1%> + <%set iremove_sub = 1%> + <%set iclose = 1%> + <%include lst_include_icons.html%> +
    +
    +

    + + + + +
    + Import results:
    + <%loop import_results%> + - <%list_name%>: + <%if overlimit%>Over subscriber limit. + <%else%> +
    +
      <%if declined != hits%><%hits - $declined%> out of<%endif%> <%hits%> email addresses imported
      + <%if invalid%><%invalid%> invalid email addresses
      <%endif%> + <%if duplicate%><%duplicate%> duplicate email addresses
      <%endif%> +
    + <%endif%> + <%endloop%> +

    +
    +
    + + <%loop import_results%> + <%loop results%> + + + + + + <%endloop%> + <%endloop%> +
    <%sub_email%><%list_name%> [ <%if status%><%status%><%else%>Imported<%endif%> ]
    +
    +
    +
    +
    +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/lst_include_icons.html b/site/glist/templates/gossamer/lst_include_icons.html new file mode 100644 index 0000000..59d636a --- /dev/null +++ b/site/glist/templates/gossamer/lst_include_icons.html @@ -0,0 +1,20 @@ +<%if sub_list_id_fk%><%set lst_id = $sub_list_id_fk%><%endif%> +<%set separator = '   '%> + +<%ifnot mn_subscriber%> +<%if imodify%>
    Edit<%else%>Edit<%endif%>  +<%separator%><%if ihtml%>HTML<%else%>HTML<%endif%>  +<%separator%><%if isub_import%>Import<%else%>Import<%endif%>  +  <%if iclose%><%endif%> +<%else%> + <%if isub_search%>Search<%else%>Search<%endif%>  + <%separator%><%if isub_add%>Add<%else%>Add<%endif%>  + <%separator%><%if isub_import%>Import<%else%>Import<%endif%>  + <%separator%><%if iremove_sub%>Remove Bounced<%else%>Remove Bounced<%endif%>  + <%if iclose%> +   + <%endif%> +<%endif%> + diff --git a/site/glist/templates/gossamer/lst_include_menu.html b/site/glist/templates/gossamer/lst_include_menu.html new file mode 100644 index 0000000..585ee93 --- /dev/null +++ b/site/glist/templates/gossamer/lst_include_menu.html @@ -0,0 +1,12 @@ + + + + + + + + + + + +
     ListsCreate ListFind ListSubscribers
    diff --git a/site/glist/templates/gossamer/lst_include_record.html b/site/glist/templates/gossamer/lst_include_record.html new file mode 100644 index 0000000..24d073c --- /dev/null +++ b/site/glist/templates/gossamer/lst_include_record.html @@ -0,0 +1,126 @@ + + + + + + + + + + + <%-- + + + + + --%> + <%set tpl_opt_in = GList::Tools::_current_select('lst_opt_template')%> + <%if tpl_opt_in%> + + + + + + + + + <%endif%> + <%set tpl_subscribe = GList::Tools::_current_select('lst_subs_template')%> + <%if tpl_subscribe%> + + + + + <%endif%> + <%set tpl_unsubscribe = GList::Tools::_current_select('lst_unsubs_template')%> + <%if tpl_unsubscribe%> + + + + + <%endif%> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    List Name + * +
    Description + +
    Move to List + <%fetch_lists%> + <%set selected_list_id = $lst_move_to_id_fk%> + +
    Require Double Opt In + checked<%endif%> />Yes + checked<%endif%> />No +
    Opt In Template<%tpl_opt_in%>
    Subscribe Template<%tpl_subscribe%>
    Unsubscribe Template<%tpl_unsubscribe%>
    Success URLs
    - Subscribe + +
    - Unsubscribe + +
    - Validate + +
    Failure URLs
    - Subscribe + +
    - Unsubscribe + +
    *: required
    diff --git a/site/glist/templates/gossamer/lst_include_results.html b/site/glist/templates/gossamer/lst_include_results.html new file mode 100644 index 0000000..4ab8fa2 --- /dev/null +++ b/site/glist/templates/gossamer/lst_include_results.html @@ -0,0 +1,84 @@ + + + + + + + +
    + + + + + + + + + + <%loop results%> + <%if bounced_emails%><%set bounced = 1%><%endif%> + + + + + + + + + <%endloop%> +
    + List Name + <%if sb eq 'lst_title' or not $sb%> + + <%endif%> + + Created + <%if sb eq 'lst_date_created'%> + + <%endif%> + + Subscribers + <%if sb eq 'subscribers'%> + + <%endif%> + + Validated + <%if sb eq 'validateds'%> + + <%endif%> + + Bounced + <%if sb eq 'bounceds'%> + + <%endif%> +
    + <%if lst_user_id_fk and lst_user_id_fk ne $usr_username%> + <%set user_search = 1%> + <%elsif lst_user_id_fk eq $usr_username and lst_user_id_fk-opt eq '<>'%> + <%set user_search = 1%> + <%endif%> + <%lst_title%> + <%if lst_date_created%><%GList::HTML::date_get('lst_date_created')%><%endif%><%if subscribers%><%subscribers%><%else%>0<%endif%><%if validateds%><%validateds%><%else%>0<%endif%><%if bounceds%><%bounceds%><%else%>0<%endif%> + + +
    + <%if hits%> + + <%if hits > $mh and $mh != -1%> + + + + <%endif%> + + + +
    <%include include_toolbar.html%>
    <%if msg%><%msg%><%else%> <%endif%> + + <%if bounced%> + + <%endif%> + +
    + <%endif%> +
    + diff --git a/site/glist/templates/gossamer/lst_modify_form.html b/site/glist/templates/gossamer/lst_modify_form.html new file mode 100644 index 0000000..8315cbd --- /dev/null +++ b/site/glist/templates/gossamer/lst_modify_form.html @@ -0,0 +1,60 @@ +<%set set_focus = 'myform.lst_title'%> +<%set book_mark = 'modify_list'%> +<%include include_header.html%> + + + + + + + + + +
    <%include lst_include_menu.html%>
    +
    + + + + + + + +
    + + + + + +
    Modify List + <%set iimport = 1%> + <%set ihtml = 1%> + <%set isearch_sub = 1%> + <%set iremove_sub = 1%> + <%set isub_import = 1%> + <%set iclose = 1%> + <%include lst_include_icons.html%> +
    + <%if msg%>


      <%msg%>

    <%else%>
    <%endif%> + + + + + + + +
    + + <%include lst_include_record.html%> +
    + + + +
    +
    + + <%hidden_objects%> +
    +
    +<%include include_footer.html%> + + diff --git a/site/glist/templates/gossamer/lst_search_form.html b/site/glist/templates/gossamer/lst_search_form.html new file mode 100644 index 0000000..ecc8064 --- /dev/null +++ b/site/glist/templates/gossamer/lst_search_form.html @@ -0,0 +1,123 @@ +<%set set_focus = 'myform.lst_id'%> +<%set book_mark = 'search_list'%> +<%include include_header.html%> + + + + + + + + + +
    <%include lst_include_menu.html%>
    + + + + + + + +
    + + + + + +
    Find ListClose
    +
    + + + + +
    + <%if msg%>
    <%msg%>
    <%endif%> + + + + + + + + + + + + + + + + + + + + + <%if usr_type%> + + + + + <%endif%> + + + + + + + + + + + + + + + + + + + +

  • Search Info
  • List ID + + +
    List Name + +
    Description + +
    Created
    + + + + + +
    From
     To
      <%GList::language('SYS_DATE_FORMAT')%>
    +
    Username + + +
  • Search Options
  • Keyword search
    Maximum hitsMatch Any:
    Sort by + + +

    + + +

    +
    + + <%hidden_objects%> +
    +
    +
    +<%include include_footer.html%> + diff --git a/site/glist/templates/gossamer/lst_sub_add.html b/site/glist/templates/gossamer/lst_sub_add.html new file mode 100644 index 0000000..cab43da --- /dev/null +++ b/site/glist/templates/gossamer/lst_sub_add.html @@ -0,0 +1,57 @@ +<%set set_focus = 'myform.import_to'%> +<%set book_mark = 'add_subscribers'%> +<%include include_header.html%> + + + + + + + + + +
    <%include lst_include_menu.html%>
    +
    + + + + + + + +
    + + + + + +
    Add a Subscriber + <%set mn_subscriber = 1%> + <%set isub_search = 1%> + <%set isub_import = 1%> + <%set iremove_sub = 1%> + <%set iclose = 1%> + <%include lst_include_icons.html%> +
    + <%if msg%>


      <%msg%>

    <%else%>
    <%endif%> + + + + + + + +
    + <%include lst_sub_include_record.html%> +
    + + +
    +
    + + <%hidden_objects%> +
    +
    +<%include include_footer.html%> + + diff --git a/site/glist/templates/gossamer/lst_sub_include_record.html b/site/glist/templates/gossamer/lst_sub_include_record.html new file mode 100644 index 0000000..b8808e0 --- /dev/null +++ b/site/glist/templates/gossamer/lst_sub_include_record.html @@ -0,0 +1,48 @@ + + <%if do eq 'lst_sub_modify'%> + + + + + + + + + + + + + + <%else%> + + + + + <%endif%> + + + + + + + + + + + +
    List<%lst_title%>
    Validated + +
    Bounced + +
    List + * +
    Email address + * +
    Name + +
    *: required
    diff --git a/site/glist/templates/gossamer/lst_sub_modify.html b/site/glist/templates/gossamer/lst_sub_modify.html new file mode 100644 index 0000000..c23b96e --- /dev/null +++ b/site/glist/templates/gossamer/lst_sub_modify.html @@ -0,0 +1,58 @@ +<%set set_focus = 'myform.new_email'%> +<%set book_mark = 'edit_subscribers'%> +<%include include_header.html%> + + + + + + + + + +
    <%include lst_include_menu.html%>
    +
    + + + + + + + +
    + + + + + +
    Modify a Subscriber + <%set mn_subscriber = 1%> + <%set isub_search = 1%> + <%set isub_add = 1%> + <%set isub_import = 1%> + <%set iremove_sub = 1%> + <%set iclose = 1%> + <%include lst_include_icons.html%> +
    + <%if msg%>


      <%msg%>

    <%else%>
    <%endif%> + + + + + + + +
    + <%include lst_sub_include_record.html%> +
    + + +
    +
    + + <%hidden_objects%> +
    +
    +<%include include_footer.html%> + + diff --git a/site/glist/templates/gossamer/lst_sub_success.html b/site/glist/templates/gossamer/lst_sub_success.html new file mode 100644 index 0000000..fe09070 --- /dev/null +++ b/site/glist/templates/gossamer/lst_sub_success.html @@ -0,0 +1,55 @@ +<%set set_focus = 'myform.sub_email'%> +<%set book_mark = 'modify_list'%> +<%include include_header.html%> + + + + + + + + + +
    <%include lst_include_menu.html%>
    + + + + + + + +
    + + + + + +
    Add a Subscriber + <%set mn_subscriber = 1%> + <%set isub_search = 1%> + <%set isub_import = 1%> + <%set iremove_sub = 1%> + <%set iclose = 1%> + <%include lst_include_icons.html%> +
    +
    +
    + + + + + <%loop results%> + + + + + + <%endloop%> +

    <%if msg%><%msg%>

    <%endif%>
    <%sub_email%><%lst_title%>  [ <%if status%><%status%><%else%>Added<%endif%> ]
    +
    +
    +
    +
    +<%include include_footer.html%> + + diff --git a/site/glist/templates/gossamer/lst_subscriber_form.html b/site/glist/templates/gossamer/lst_subscriber_form.html new file mode 100644 index 0000000..992d719 --- /dev/null +++ b/site/glist/templates/gossamer/lst_subscriber_form.html @@ -0,0 +1,122 @@ +<%set set_focus = 'myform.sub_list_id_fk'%> +<%set book_mark = 'search_subscriber'%> +<%include include_header.html%> + + + + + + + + + +
    <%include lst_include_menu.html%>
    +
    + + + + + + + +
    + + + + + +
    Find Subscriber + <%set mn_subscriber = 1%> + <%set isub_add = 1%> + <%set isub_import = 1%> + <%set iremove_sub = 1%> + <%set iclose = 1%> + <%include lst_include_icons.html%> +
    +
    + <%if msg%>

    <%msg%>

    <%endif%>
    + + + + + + + +
      + + + + + + + + + + + + + + + + + + <%if usr_type%> + + + + + <%endif%> + + + + +
      List + +    + +
      Subscribed + + + + + +
      From:
       To:
        <%GList::language('SYS_DATE_FORMAT')%>
      +
      Subscriber Email
      Subscriber Name
      Username + + +
      Sort by + + +
    +

    + + +

    +
    + + + <%hidden_objects%> +
    +
    +<%include include_footer.html%> + diff --git a/site/glist/templates/gossamer/lst_subscribers.html b/site/glist/templates/gossamer/lst_subscribers.html new file mode 100644 index 0000000..7e7f2a5 --- /dev/null +++ b/site/glist/templates/gossamer/lst_subscribers.html @@ -0,0 +1,168 @@ + + + <%include include_header.html%> + + + + + + + + + +
    <%include lst_include_menu.html%>
    + + + + + + + +
    + + + + + +
    Subscribers + <%set mn_subscriber = 1%> + <%set isub_add = 1%> + <%set isub_import = 1%> + <%set iremove_sub = 1%> + <%include lst_include_icons.html%> +

    +
    + + + + + + + + + +
    Search + + By  + + in  + + + [ Advanced ] +
    Quick Search<%search_bar%>
    + + <%hidden_objects%> +
    + <%if msg%>

    <%msg%>

    <%endif%> +
    + <%if lst_user_id_fk and lst_user_id_fk ne $usr_username%> + <%set user_search = 1%> + <%elsif lst_user_id_fk eq $usr_username and lst_user_id_fk-opt eq '<>'%> + <%set user_search = 1%> + <%endif%> + <%if hits > $mh and $mh != -1%> + + + + +
    <%include include_toolbar.html%>
    + <%endif%> + + + + +
    + + + + + + + + + + <%loop results%> + + + + + + + + + + <%endloop%> +
    + <%ifnot hits%>Email + <%else%> + Email + <%ifnot sb%> + + <%endif%> + <%endif%> + + <%ifnot hits%>Name + <%else%> + Name + <%if sb eq 'sub_name'%> + + <%endif%> + <%endif%> + + <%ifnot hits%>List + <%else%> + List + <%if sb eq 'sub_list_id_fk'%> + + <%endif%> + <%endif%> + + <%ifnot hits%>Validated + <%else%> + Validated + <%if sb eq 'sub_validated'%> + + <%endif%> + <%endif%> + + <%ifnot hits%>Bounced + <%else%> + Bounced + <%if sb eq 'sub_bounced'%> + + <%endif%> + <%endif%> +
    <%sub_email%><%if sub_name%><%sub_name%><%else%> <%endif%><%GList::Tools::list_title($sub_list_id_fk)%><%if sub_validated%>Yes<%else%>No<%endif%><%if sub_bounced%><%sub_bounced%><%else%>0<%endif%>
    +
    + <%if hits%> + + <%if hits > $mh and $mh != -1%> + + + + <%endif%> + + + +
    <%include include_toolbar.html%>
    + + + +
    + <%endif%> + + + + <%hidden_objects%> +
    +
    +
    +<%include include_footer.html%> + + diff --git a/site/glist/templates/gossamer/lst_subscribers_preview.html b/site/glist/templates/gossamer/lst_subscribers_preview.html new file mode 100644 index 0000000..7eea1aa --- /dev/null +++ b/site/glist/templates/gossamer/lst_subscribers_preview.html @@ -0,0 +1,71 @@ + + + <%include include_style.txt%> + + +<%if msg%>

    <%msg%>

    <%endif%> + + + + +
    + + + + + + + + <%loop results%> + + + + + + + + + <%endloop%> +
    + <%ifnot hits%>Email + <%else%> + Email + <%ifnot sb%> + + <%endif%> + <%endif%> + + <%ifnot hits%>Name + <%else%> + Name + <%if sb eq 'sub_name'%> + + <%endif%> + <%endif%> + + <%ifnot hits%>Validated + <%else%> + Validated + <%if sb eq 'sub_validated'%> + + <%endif%> + <%endif%> + + <%ifnot hits%>Bounced + <%else%> + Bounced + <%if sb eq 'sub_bounced'%> + + <%endif%> + <%endif%> +
    <%sub_email%><%if sub_name%><%sub_name%><%else%> <%endif%><%if sub_validated%>Yes<%else%>No<%endif%><%if sub_bounced%>Yes<%else%>No<%endif%>
    +
    + + <%if hits > $mh%> + + + + <%endif%> +
    <%include include_toolbar.html%>
    + + diff --git a/site/glist/templates/gossamer/lst_unsub_bounced.html b/site/glist/templates/gossamer/lst_unsub_bounced.html new file mode 100644 index 0000000..4ac3c72 --- /dev/null +++ b/site/glist/templates/gossamer/lst_unsub_bounced.html @@ -0,0 +1,160 @@ + + + + <%include include_header.html%> + + + + + + + + + +
    <%include lst_include_menu.html%>
    + + + + + + + +
    + + + + + +
    Bounced Emails + <%set mn_subscriber = 1%> + <%set isub_add = 1%> + <%set isub_import = 1%> + <%set isub_search = 1%> + <%set iclose = 1%> + <%include lst_include_icons.html%> +

    +
    + + + + +
    + Number of bounces:
    + + +
    + + <%hidden_objects%> +
    + + <%if msg%><%endif%> + <%if hits > $mh and $mh != -1%> + + + + <%endif%> +
    <%msg%>
    <%include include_toolbar.html%>
    +
    + + + + +
    + + + + + + + + + <%loop results%> + + + + + + + + + <%endloop%> +
    + <%ifnot hits%>Email + <%else%> + Email + <%ifnot sb%> + + <%endif%> + <%endif%> + + <%ifnot hits%>Name + <%else%> + Name + <%if sb eq 'sub_name'%> + + <%endif%> + <%endif%> + + <%ifnot hits%>List + <%else%> + List + <%if sb eq 'sub_list_id_fk'%> + + <%endif%> + <%endif%> + + <%ifnot hits%>Bounced + <%else%> + Bounced + <%if sb eq 'sub_bounced'%> + + <%endif%> + <%endif%> +
    <%sub_email%><%if sub_name%><%sub_name%><%else%> <%endif%><%GList::Tools::list_title($sub_list_id_fk)%><%if sub_bounced%><%sub_bounced%><%else%>0<%endif%>
    +
    + <%if hits%> + + <%if hits > $mh and $mh != -1%> + + + + <%endif%> + + + +
    <%include include_toolbar.html%>
    + + + +
    + <%endif%> + + + + + <%hidden_objects%> +
    +
    +
    +<%include include_footer.html%> + + diff --git a/site/glist/templates/gossamer/mailarc_about.html b/site/glist/templates/gossamer/mailarc_about.html new file mode 100644 index 0000000..e5e35ee --- /dev/null +++ b/site/glist/templates/gossamer/mailarc_about.html @@ -0,0 +1,124 @@ +<%include include_header.html%> + + + + + + + + + +
    <%include plugin_include_menu.html%>
    + + + + + + + +
    + + + + + + + +
    + + + + +
    + + + + + +
    <%if gossamer%>Install Plugins from Gossamer<%else%>Plugin Manager<%endif%>
    +
    +
    + <%GList::Plugins::admin_menu%> + + + <%if menu%> + + <%endif%> + + +
    + <%menu%>
    +

    + + + + +
    +

    MailArc + Plugin

    +

    This plugin will allow + you to use Gossamer List as an archive for mailing list.
    +

    +

    + Mail gateway configuration:

    +
    +

    The following instructions + are for the configuration of a system using Unix/Linux and sendmail.

    +
    +
      +
    • + The MailArc plugin allows you to send a message from your desktop email client (Outlook) to GList, and GList will forward the message out to the lists within GList. +
      +
      + We must then edit the sendmail aliases file. Often, it is located at + "/etc/aliases". We will take advantage of the facility to + associate a program with any arbitrary email address. For example: If + we want to create a special listener on the listowner_5 + mailing list with the email address listowner_\@somehost.com (where + somehost.com is your server's domain name) we can enter the following + line into your aliases file. You will probably have to be root to make + this change.
      +
      + listowner_5: "|$CFG->{priv_path}/archive.pl"
      +
      +
      + Up to the ":" is the the username for the new email address. + The rest of the configuration string tells sendmail to invoke the script + "archive.pl" every time an email comes in for that user.
      +
      + To make sure that your mail server is able to use this change, you must + convert the "aliases" configuration file into a format that + sendmail can understand. To do so, run the command "sendmail -bi" + as root or as the mail system administrator.
      +
      + [root\@penguin GList]# sendmail -bi
      + /etc/aliases: 20 aliases, longest 70 bytes, 307 bytes total
      + Jan 17 09:50:49 192 sendmail[9398]: alias database /etc/aliases rebuilt + by root

      +
      + Don't forget sign up this new email address to your mailing-list.
      +
    • +
    • If + you do not have the level of control to make changes to the "aliases" + file or are under a different system, the "archive.pl" script + runs as a mail pipe accepting emails from STDIN. Contact your system administrator + for the best option.
      +

      +
    • +
    +

    + MailArc POP3 Guide :

    +
      +

      + You may use MailArc to pull out all emails from a POP3 account and archive them into your GList. + The command below should be run directly from your command line or should be set up as a cron job:
      +
      + archive.pl --forum=x --pop-host=your_pop_host --pop-user=your_pop_user --pop-pass=your_pop_pass +
      +

      +
    + +
    +
    +
    +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/mli_check_bounced_form.html b/site/glist/templates/gossamer/mli_check_bounced_form.html new file mode 100644 index 0000000..a5def9f --- /dev/null +++ b/site/glist/templates/gossamer/mli_check_bounced_form.html @@ -0,0 +1,96 @@ +<%set set_focus = 'myform.mail_host'%> +<%set book_mark = 'check_bounced'%> +<%include include_header.html%> +<%set use_magic = 1%> + + + + + + + + + + + + +
    <%include mli_include_menu.html%>
    <%include mli_include_nav.html%> + + + + + + + + + <%hidden_objects%> + +
    + + + + +
    + + + + + +
    Check for bounced emails
    +
    + + + + + + + +

    +

      <%if msg%><%msg%><%else%>From here you can scan a POP box for undeliverable mail and mark +subscribers as bounced.<%endif%>

    + + + + + + + + + + + + + + + + + + + + + + + + + +
    Server mail (POP3) + +
    Port + +
    Account name + +
    Password + +
    Delete Emails + None + Bounced + All +
      + Remember Settings +
    +

    + +
    +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/mli_check_bounced_results.html b/site/glist/templates/gossamer/mli_check_bounced_results.html new file mode 100644 index 0000000..cb88a69 --- /dev/null +++ b/site/glist/templates/gossamer/mli_check_bounced_results.html @@ -0,0 +1,46 @@ +<%set book_mark = 'check_bounced'%> +<%include include_header.html%> + + + + + + + + + + + +
    <%include mli_include_menu.html%>
    <%include mli_include_nav.html%> + + + + + + + +
    + + + + +
    + + + + + +
    Check for bounced emails
    +


    + + + + +
    Bounced email results

    + - Total checked email(s): <%if parsed%><%parsed%><%else%>0<%endif%>
    + - Total bounced email(s): <%if bounced%><%bounced%><%else%>0<%endif%>
    + - Total deleted email(s): <%if deleted%><%deleted%><%else%>0<%endif%> +
    +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/mli_home.html b/site/glist/templates/gossamer/mli_home.html new file mode 100644 index 0000000..4bcc3c0 --- /dev/null +++ b/site/glist/templates/gossamer/mli_home.html @@ -0,0 +1,36 @@ +<%if p%> + <%include mli_include_preview.html%> +<%else%> +<%if fd == 2%><%set set_focus = 'myform.cm_name'%> +<%elsif fd == 1 or $fd eq ''%><%set call_check = 1%><%endif%> + <%include include_header.html%> + <%set use_magic = 1%> + + + + + + + + + + + + + +
    <%include mli_include_menu.html%>
    <%include mli_include_nav.html%> + <%if d%> + <%include mli_include_details.html%> + <%else%> + <%include mli_include_results.html%> + <%endif%> + + + + + + + <%hidden_query%> +
    + <%include include_footer.html%> +<%endif%> diff --git a/site/glist/templates/gossamer/mli_include_details.html b/site/glist/templates/gossamer/mli_include_details.html new file mode 100644 index 0000000..8a840a5 --- /dev/null +++ b/site/glist/templates/gossamer/mli_include_details.html @@ -0,0 +1,114 @@ +<%loop results%> + + + + + + + +
    + + + + +
    + + + + + +
    Mailing Details + <%set separator = '   '%> + <%if mli_scheduled%>Edit Schedule<%endif%> <%separator%> + Preview   + Close +
    +
    + + + + + <%if mli_done%> + + + + <%endif%> + + + + + + + + + + + + + + + + + + + + <%if mli_charset%> + + + + + <%endif%> + + + + + <%GList::HTML::generate_attachments('mli_id')%> + <%if attachments%> + + + + + <%endif%> + <%if mli_message_text%> + + + + <%endif%> + <%if mli_message_html%> + + + + <%endif%> +
    Mailing ID: <%mli_id%>
    Mailing Completed: <%GList::HTML::date_get('mli_done','%dddd%, %mmmm% %dd%, %yyyy%, %hh%:%mm%:%ss%')%> by <%mli_user_id_fk%>
     
    To: + <%total%> recipient<%if total > 1%>s<%endif%> +
    From:<%if mli_name%>"<%mli_name%>" < <%mli_from%> ><%else%><%mli_from%><%endif%>
    Reply To:<%mli_reply_to%>
    Return-path:<%mli_bounce_email%>
    Charset:<%mli_charset%>
    Subject:<%mli_subject%>
    Attachments: + + <%loop attachments%> + + + + + <%endloop%> +
    <%mat_file_name%> (<%GList::Tools::friendly_size($mat_file_size)%>) + view + download +
    +
    Text Message:
    + + + + +
    +
    <%GList::HTML::html_unescape($mli_message_text)%>
    +
    +
    HTML Message:
    + + +
    <%mli_message_html%>
    +
    +
    +<%endloop%> + + diff --git a/site/glist/templates/gossamer/mli_include_menu.html b/site/glist/templates/gossamer/mli_include_menu.html new file mode 100644 index 0000000..5d44da0 --- /dev/null +++ b/site/glist/templates/gossamer/mli_include_menu.html @@ -0,0 +1,11 @@ + + + + + + + + + + +
     MailingsFind MailingCheck Bounced
    diff --git a/site/glist/templates/gossamer/mli_include_nav.html b/site/glist/templates/gossamer/mli_include_nav.html new file mode 100644 index 0000000..ee130b4 --- /dev/null +++ b/site/glist/templates/gossamer/mli_include_nav.html @@ -0,0 +1,155 @@ + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    + <%if cm_name and $do%> + + <%else%> + <%if fd == 3 or fd == 4%> + + <%elsif fd == 2%> + + <%else%> + + <%endif%> + <%endif%> + + <%if cm_name and $do%> + <%cm_name%> + <%else%> + <%if fd == 4%> + Scheduled Mailings + <%elsif fd == 3%> + Deleted Mailings + <%elsif fd == 2%> + Sent Mailings + <%else%> + Queued Mailings + <%endif%> + <%endif%> +
    +
    +
    + + + + + <%if fd == 2 and not $users%> + + + + <%endif%> +
    + + + + + + + + + + + + + + + + + <%if cs and $hits_sent%> + + + + + <%endif%> + + + + + +
    + <%if $fd <= 1%>Queued Mailings<%if drafts%> (<%drafts%>)<%endif%><%else%>Queued Mailings<%if drafts%> (<%drafts%>)<%endif%><%endif%> +
    + <%if fd == 4%><%endif%>Scheduled Mailings<%if scheduled_hits%> (<%scheduled_hits%>)<%endif%><%if fd==4%><%endif%> +
    + <%if hits_sent%> + + <%endif%> + + <%if id or fd ne '2'%> + Sent Items + <%else%> + Sent Items + <%endif%> +
      + + <%loop results_sent%> + + + + + + <%endloop%> +
    + <%if id eq $cm_id%> + <%cm_name%> + <%else%> + <%cm_name%> + <%endif%> +
    +
    + <%if hits_deleted%> + + <%endif%> + + <%if fd == 3%> + Deleted Items + <%else%> + Deleted Items + <%endif%> +
    +

    + <%ifnot id%> + Name: + + + <%else%> + + + + + + + + + +
    Name: + +
      + + +
    + <%endif%> +
    +
    diff --git a/site/glist/templates/gossamer/mli_include_preview.html b/site/glist/templates/gossamer/mli_include_preview.html new file mode 100644 index 0000000..c921dbf --- /dev/null +++ b/site/glist/templates/gossamer/mli_include_preview.html @@ -0,0 +1,86 @@ + +<%include include_style.txt%> + +<%loop results%> + + + + +
    + + + + + <%if mli_done%> + + + + <%endif%> + + + + + + + + + + + + + + + + + + + + <%if mli_charset%> + + + + + <%endif%> + + + + + <%GList::HTML::generate_attachments('mli_id')%> + <%if attachments%> + + + + + <%endif%> + <%if mli_message_text%> + + + + <%endif%> + <%if mli_message_html%> + + + + <%endif%> +
    Mailing ID: <%mli_id%>
    Mailing Completed: <%GList::HTML::date_get('mli_done','%dddd%, %mmmm% %dd%, %yyyy%, %hh%:%mm%:%ss%')%>
     
    To: + <%total%> recipient<%if total > 1%>s<%endif%> +
    From:<%if mli_name%>"<%mli_name%>" < <%mli_from%> ><%else%><%mli_from%><%endif%>
    Reply To:<%mli_reply_to%>
    Return-path:<%mli_bounce_email%>
    Charset:<%mli_charset%>
    Subject:<%mli_subject%>
    Attachments: + + <%loop attachments%> + + + + + <%endloop%> +
    <%mat_file_name%> (<%GList::Tools::friendly_size($mat_file_size)%>) + view + download +
    +
    Text Message:
    + <%GList::HTML::html_unescape($mli_message_text)%> +

    HTML Message:
    + <%mli_message_html%> +
    +
    +<%endloop%> + diff --git a/site/glist/templates/gossamer/mli_include_results.html b/site/glist/templates/gossamer/mli_include_results.html new file mode 100644 index 0000000..6ca0e42 --- /dev/null +++ b/site/glist/templates/gossamer/mli_include_results.html @@ -0,0 +1,225 @@ +<%ifnot fd%><%set fd = 1%><%endif%> +<%if fd == 4%> + + + + + + + + + + +
    + + + + + + + + + + + + + <%loop results%> + + + + + + + <%GList::Tools::schedule_info($mli_id)%> + + + + + + <%endloop%> +
    + <%ifnot hits%>ID + <%else%> + ID + <%if sb eq 'mli_id' or $sb eq ''%> + + <%endif%> + <%endif%> + + <%ifnot hits%>Subject + <%else%> + Subject + <%if sb eq 'mli_subject'%> + + <%endif%> + <%endif%> + RecipientsFrequencyTimeStatuschecked<%endif%>/>
    + <%GList::HTML::generate_attachments('mli_id')%> + <%if attachments%><%else%> <%endif%> + <%mli_id%> + <%if mli_user_id_fk and mli_user_id_fk ne $usr_username%> + <%set user_search = 1%> + <%elsif mli_user_id_fk eq $usr_username and mli_user_id_fk-opt eq '<>'%> + <%set user_search = 1%> + <%endif%> + <%mli_subject%> + <%if total%><%total%><%endif%> + <%if scm_type == 1%>One on <%scm_option%> + <%elsif scm_type == 2%>Daily + <%elsif scm_type == 3%>Weekly on <%scm_option%> + <%elsif scm_type == 4%>Monthly on <%scm_option%><%endif%> + <%scm_hour%>:<%if scm_minute < 10%>0<%endif%><%scm_minute%> <%if scm_hour <= 12%>AM<%else%>PM<%endif%> + <%if GList::Tools::schedule_status%>Sent<%endif%> + + checked<%endif%>> + +
    +
    + <%if msg%><%msg%><%endif%> + <%if hits%> + + <%if hits > $mh%> + + + + <%endif%> + + + +
    <%include include_toolbar.html%>
    + + + +
    + <%endif%> +
    +<%else%> + + + + + + + + + + +
    + + + + + + + + + + + + + + <%loop results%> + + + + + + + + + + + + + <%endloop%> +
    + <%ifnot hits%>ID + <%else%> + ID + <%if sb eq 'mli_id' or $sb eq ''%> + + <%endif%> + <%endif%> + + <%ifnot hits%>Subject + <%else%> + Subject + <%if sb eq 'mli_subject'%> + + <%endif%> + <%endif%> + RecipientsSentBouncedOpenedClickedchecked<%endif%>/>
    + <%GList::HTML::generate_attachments('mli_id')%> + <%if attachments%><%else%> <%endif%> + <%mli_id%> + <%if mli_user_id_fk and mli_user_id_fk ne $usr_username%> + <%set user_search = 1%> + <%elsif mli_user_id_fk eq $usr_username and mli_user_id_fk-opt eq '<>'%> + <%set user_search = 1%> + <%endif%> + <%mli_subject%> + <%if total%><%total%><%endif%><%if done%><%done%><%endif%><%if bounced_emails%><%bounced_emails%><%endif%><%if mli_num_opened%><%mli_num_opened%><%endif%><%if mli_num_clicked%><%mli_num_clicked%><%endif%> + checked<%endif%>> + +
    +
    + <%if msg%><%msg%><%endif%> + <%if hits%> + + <%if hits > $mh%> + + + + <%endif%> + + + + +
    <%include include_toolbar.html%>
      + <%ifnot users%> + <%if fd == 1 and hits_draft%> + <%set flag = 1%> + <%elsif fd = 2 and hits_sent%> + <%set flag = 1%> + <%elsif fd = 3%> + <%set flag = 1%> + <%elsif id%> + <%set flag = 1%> + <%endif%> + <%if flag%> + <%if fd == 3%>Restore<%else%>Move<%endif%> to: + + + <%endif%> + <%endif%> + <%if fd <=1 %> + + + <%endif%> + + <%if fd == 3%><%endif%> +
    + <%endif%> +
    +<%endif%> diff --git a/site/glist/templates/gossamer/mli_include_schedule_mailing.html b/site/glist/templates/gossamer/mli_include_schedule_mailing.html new file mode 100644 index 0000000..5abe434 --- /dev/null +++ b/site/glist/templates/gossamer/mli_include_schedule_mailing.html @@ -0,0 +1,132 @@ + + + + + + + + + + + + + +
    Time Of Day:
    +
      : + +
    Frequency:
      + + + + + + + + + + + + + + + + + + + + + + + +
      checked<%endif%> value="1">Once on + <%GList::language('SYS_DATE_FORMAT')%> +
      checked<%endif%> value="2">Daily
      checked<%endif%> value="3">Weekly on + +
      checked<%endif%>>Monthly + +
      Update text part with content from the following URL + +
      Update html part with content from the following URL + +
      +
    diff --git a/site/glist/templates/gossamer/mli_modify_schedule.html b/site/glist/templates/gossamer/mli_modify_schedule.html new file mode 100644 index 0000000..de9d118 --- /dev/null +++ b/site/glist/templates/gossamer/mli_modify_schedule.html @@ -0,0 +1,83 @@ +<%set book_mark = 'search_mailing'%> +<%include include_header.html%> + + + + + + + + + + + + +
    <%include mli_include_menu.html%>
    <%include mli_include_nav.html%> + + + + + + + +
    + + + + +
    + + + + + +
    Modify Scheduled Mailing + <%set separator = '   '%> + Edit Schedule <%separator%> + Preview   + Close +
    +
    +
    + + + + + + + + +
    +
    +
    <%msg%>

    +  Mailing ID: <%mli_id%> - <%mli_subject%>

    + <%include mli_include_schedule_mailing.html%> +
    +
    + + + + + + + + <%hidden_objects%> +
    +
    + + + +
    +
    +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/mli_progress_bar.html b/site/glist/templates/gossamer/mli_progress_bar.html new file mode 100644 index 0000000..960b4be --- /dev/null +++ b/site/glist/templates/gossamer/mli_progress_bar.html @@ -0,0 +1,43 @@ +<%include include_header.html%> + + + + +
    + + + + + + + + + + +
    + + + + +
    +
    + + + + + +
    +
    Preparing....
    +
    +
    0%
    +
    +
    +
    + + + + diff --git a/site/glist/templates/gossamer/mli_recipients.html b/site/glist/templates/gossamer/mli_recipients.html new file mode 100644 index 0000000..f6ec1e1 --- /dev/null +++ b/site/glist/templates/gossamer/mli_recipients.html @@ -0,0 +1,28 @@ + + + <%if site_title%><%site_title%><%else%>Gossamer Gossamer List<%endif%> - Recipients + <%include include_style.txt%> + + + + + + + + + + + <%if hits > $mh%> + + + + <%endif%> +
    Mailing Recipients

    Mailing ID: <%eml_mailing_id_fk%>
    + <%loop results%> + <%eml_email%><%if eml_skipped%> [ Skipped ]<%endif%>
    + <%endloop%> +

    <%include include_toolbar.html%>
    + + + + diff --git a/site/glist/templates/gossamer/mli_schedule_mailing.html b/site/glist/templates/gossamer/mli_schedule_mailing.html new file mode 100644 index 0000000..d902952 --- /dev/null +++ b/site/glist/templates/gossamer/mli_schedule_mailing.html @@ -0,0 +1,70 @@ +<%set book_mark = 'search_mailing'%> +<%include include_header.html%> + + + + + + + + + + + +
    <%include mli_include_menu.html%>
    <%include mli_include_nav.html%> + + + + + + + +
    + + + + +
    + + + + + +
    Mailing ScheduleClose
    +
    +
    + + + + + + + + +
    +
    +
    <%msg%>
      + <%loop selected_mailings%> + + + - <%mli_id%>: <%mli_subject%>
      + <%endloop%> +
    + <%include mli_include_schedule_mailing.html%> +
    +
    + + + + + + <%hidden_objects%> +
    +
    + + +
    +
    +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/mli_search_form.html b/site/glist/templates/gossamer/mli_search_form.html new file mode 100644 index 0000000..a703a10 --- /dev/null +++ b/site/glist/templates/gossamer/mli_search_form.html @@ -0,0 +1,159 @@ +<%set set_focus = 'myform.mli_id'%> +<%set book_mark = 'search_mailing'%> +<%include include_header.html%> + + + + + + + + + + + +
    <%include mli_include_menu.html%>
    <%include mli_include_nav.html%> + + + + + + + +
    + + + + +
    + + + + + +
    Find MailingClose
    +
    +
    + + + + +
    + <%if msg%>
    <%msg%>
    <%endif%> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%if usr_type%> + + + + + <%endif%> + + + + + + + + + + + + + + + + + + + +

  • Search Info
  • Mailing ID + + +
    Done + + + + + +
    From
     To
      <%GList::language('SYS_DATE_FORMAT')%>
    +
    Subject + +
    Message TEXT + +
    Message HTML + +
    From + +
    Name + +
    Username + + +
  • Search Options
  • Keyword search
    Maximum hitsMatch Any:
    Sort by + + +

    + + +

    + + + + + <%hidden_objects%> + +
    +
    +
    +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/msg_add_form.html b/site/glist/templates/gossamer/msg_add_form.html new file mode 100644 index 0000000..de0765b --- /dev/null +++ b/site/glist/templates/gossamer/msg_add_form.html @@ -0,0 +1,122 @@ + +<%set book_mark = 'create_message'%> + +<%set set_focus = 'myform.msg_subject'%> +
    +<%include include_header.html%> + + + + + + + + + + + +
    <%include msg_include_menu.html%>
    <%include msg_include_nav.html%> + + + + + + + +
    + + + + +
    + + + + + +
    Create Message + <%set iclose = 1%> + <%include msg_include_icons.html%> +
    +
    + + + + + + + + + + +
    + <%if msg%>

    <%msg%>

    <%else%> <%endif%> +
    + <%include msg_include_record.html%> +
    + + + +
    +
    +
    + +<%hidden_objects%> +<%include include_footer.html%> +
    diff --git a/site/glist/templates/gossamer/msg_home.html b/site/glist/templates/gossamer/msg_home.html new file mode 100644 index 0000000..14cb82d --- /dev/null +++ b/site/glist/templates/gossamer/msg_home.html @@ -0,0 +1,33 @@ +<%ifnot users%><%set set_focus = 'myform.cms_name'%><%endif%> +<%if p%> + + <%include include_style.txt%> + <%if site_title%><%site_title%><%else%>Gossamer List<%endif%> + <%include msg_include_preview.html%> +<%else%> + <%include include_header.html%> + <%set use_magic = 1%> + + + + + + + + + + + + + +
    <%include msg_include_menu.html%>
    <%include msg_include_nav.html%> + <%if d%> + <%include msg_include_details.html%> + <%else%> + <%include msg_include_results.html%> + <%endif%> + + <%hidden_objects%> +
    + <%include include_footer.html%> +<%endif%> diff --git a/site/glist/templates/gossamer/msg_include_details.html b/site/glist/templates/gossamer/msg_include_details.html new file mode 100644 index 0000000..0186302 --- /dev/null +++ b/site/glist/templates/gossamer/msg_include_details.html @@ -0,0 +1,130 @@ + + + + +
    + + + + + <%loop results%> + + + + +
    + + + + +
    + + + + + +
    Message Details + <%set iclose = 1%> + <%set ipreview = 1%> + <%set isend = 1%> + <%set iedit = 1%> + <%set modify = 1%> + <%include msg_include_icons.html%> +
    +
    +
    +
    + + + + + + <%if msg_charset%> + + + + + <%endif%> + + + + + + + + + + + + + + + + + + + + + + + + + <%GList::HTML::generate_attachments%> + <%if attachments%> + + + + + <%endif%> + <%if msg_content_text%> + + + + <%endif%> + <%if msg_content_html%> + + + + <%endif%> +
    Message Mode[ + <%if msg_mode eq 'html'%>HTML + <%elsif msg_mode eq 'multi'%>Text and HTML + <%else%>Text Only<%endif%> ] +
    Charset<%msg_charset%>
    Subject<%msg_subject%>
    From<%if msg_from_name%>"<%msg_from_name%>" <<%msg_from_email%>><%else%><%msg_from_email%><%endif%>
    Reply To<%msg_reply_to%>
    Return-Path<%msg_bounce_email%>
    Status<%ifnot msg_status%>Draft<%elsif msg_status == 1%>Sent<%endif%>
    Created<%GList::HTML::date_get('msg_created', '%mm%-%dd%-%yyyy% %hh%:%MM%:%ss%')%> + <%if usr_email ne $msg_user_id_fk%>, by <%msg_user_id_fk%><%endif%> +
    Attachments + + <%loop attachments%> + + + + + <%endloop%> +
    <%att_file_name%> (<%GList::Tools::friendly_size($att_file_size)%>) + view + download +
    +
    <%if msg_mode eq 'multi' or msg_mode eq 'html'%>Text Message
    <%endif%> + + + + +
    +
    + <%GList::HTML::html_unescape($msg_content_text)%> +
    +
    +
    <%if msg_mode eq 'multi' or msg_mode eq 'html'%>HTML Message
    <%endif%> + + + + +
    +
    + <%msg_content_html%> +
    +
    +
    +
    + <%endloop%> +
    diff --git a/site/glist/templates/gossamer/msg_include_icons.html b/site/glist/templates/gossamer/msg_include_icons.html new file mode 100644 index 0000000..24aa707 --- /dev/null +++ b/site/glist/templates/gossamer/msg_include_icons.html @@ -0,0 +1,13 @@ +<%set first_link = 1%> +<%set separator = '   '%> +<%if modify%> + <%ifnot mul_messages%> + <%if idetails%>Details<%else%>Details<%endif%> + <%separator%><%if ipreview%>Preview<%endif%> + <%separator%><%if iedit%>Edit<%else%>Edit<%endif%> + <%separator%><%if isend%>Select Lists<%else%>Select Lists<%endif%> + <%endif%> +<%endif%> +   <%if iclose%><%endif%> diff --git a/site/glist/templates/gossamer/msg_include_menu.html b/site/glist/templates/gossamer/msg_include_menu.html new file mode 100644 index 0000000..096c9bd --- /dev/null +++ b/site/glist/templates/gossamer/msg_include_menu.html @@ -0,0 +1,12 @@ + + + + + + + + + + +
      MessagesCreate MessageFind Message
    + diff --git a/site/glist/templates/gossamer/msg_include_nav.html b/site/glist/templates/gossamer/msg_include_nav.html new file mode 100644 index 0000000..e936265 --- /dev/null +++ b/site/glist/templates/gossamer/msg_include_nav.html @@ -0,0 +1,95 @@ + + + + + + + + + + + + + +
    + + + + +
    + + + + + +
    <%if cms_name and $do%><%cms_name%><%else%>Folders<%endif%>
    +
    +
    + + + + + + + <%ifnot cd%> + + + + + <%endif%> + <%ifnot users%> + + + + <%endif%> +
    + <%if hits_cat%> + + <%endif%> + + <%ifnot msg_cat_id_fk%>Messages + <%else%> + Messages + <%endif%> + <%if hits_root%>(<%hits_root%>)<%endif%> +
      + + <%loop results_cat%> + + + + + + <%endloop%> +
    + <%if msg_cat_id_fk eq $cms_id%><%cms_name%> + <%else%> + <%cms_name%> + <%endif%> + <%if messages%>(<%messages%>)<%endif%> +
    +

    + <%ifnot cms_id%> +  Name: + + + <%elsif do ne 'deletecat'%> + + + + + + + + +
    Name: + +
      + + + +
    + <%endif%> +
    +
    + diff --git a/site/glist/templates/gossamer/msg_include_preview.html b/site/glist/templates/gossamer/msg_include_preview.html new file mode 100644 index 0000000..450db36 --- /dev/null +++ b/site/glist/templates/gossamer/msg_include_preview.html @@ -0,0 +1,102 @@ + +
    + + <%if msg%> + + + + <%endif%> + + + +
    <%msg%>
    Send a copy of this message to:
    + Name + Email + +
    + + + + + + <%hidden_objects%> +
    +<%loop results%> + + + + +
    + + + + + + <%if msg_charset%> + + + + + <%endif%> + + + + + + + + + + + + + + + + + + + + + <%GList::HTML::generate_attachments%> + <%if attachments%> + + + + + <%endif%> + <%if msg_content_text%> + + + + <%endif%> + <%if msg_content_html%> + + + + <%endif%> +
    Message Mode[ + <%if msg_mode eq 'html'%>HTML + <%elsif msg_mode eq 'multi'%>Text and HTML + <%else%>Text Only<%endif%> ] +
    Charset<%msg_charset%>
    Subject<%msg_subject%>
    From<%if msg_from_name%>"<%msg_from_name%>" <<%msg_from_email%>><%else%><%msg_from_email%><%endif%>
    Reply To<%msg_reply_to%>
    Return-Path<%msg_bounce_email%>
    Created<%GList::HTML::date_get('msg_created', '%mm%-%dd%-%yyyy% %hh%:%MM%:%ss%')%> + <%if usr_email ne $msg_user_id_fk%>, by <%msg_user_id_fk%><%endif%> +
    Attachments + + <%loop attachments%> + + + + + <%endloop%> +
    <%att_file_name%> (<%GList::Tools::friendly_size($att_file_size)%>) + view + download +
    +
    <%if msg_mode eq 'multi' or msg_mode eq 'html'%>Text Message
    <%endif%> + <%GList::HTML::html_unescape($msg_content_text)%> +
    <%if msg_mode eq 'multi' or msg_mode eq 'html' %>HTML Message
    <%endif%> + <%msg_content_html%> +
    +
    +<%endloop%> diff --git a/site/glist/templates/gossamer/msg_include_record.html b/site/glist/templates/gossamer/msg_include_record.html new file mode 100644 index 0000000..87e8920 --- /dev/null +++ b/site/glist/templates/gossamer/msg_include_record.html @@ -0,0 +1,381 @@ +<%include spellcheck_editor.html%> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<%if emode eq 'html' or emode eq 'multi'%> + + + + +<%endif%> + + + + +<%-- +TEXT message part +--%> +<%if emode eq 'text' or emode eq 'multi' or emode eq ''%> + + + +<%endif%> +<%-- +HTML code for message having HTML and TEXT part +--%> +<%if emode eq 'html' or emode eq 'multi'%> + + + + <%if editor_advanced%> + + + + <%else%> + + + + <%endif%> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<%endif%> +
    Email Mode[ + <%if emode eq 'html'%>HTML + <%elsif emode eq 'multi'%>Text and HTML + <%else%>Text Only<%endif%> ] + + +
    Character Set: + +
    Subject + * +
    From Name + +
    From Email + * +
    Reply to Email + * +
    Bounce Email + * +
    Attachments + + + + + <%if hits%> + + + + + <%endif%> +
    + + +
    + + <%loop attachments%> + + + + + <%endloop%> +
    + + + <%user_fname%>  (<%GList::Tools::friendly_size($fsize)%>)
    +
    +
    Text + <%if emode eq 'html'%> + [ Autogenerated ] + <%elsif emode eq 'multi'%> + [ Entered Manually ] + <%endif%> + +
    <%if emode ne 'html'%>Text message<%else%> <%endif%>*: required 
    + +
    <%if editor_advanced%>HTML message<%else%>Raw HTML<%endif%>  [ <%if editor_advanced%>Use Text Area<%else%>Use Advanced Editor<%endif%> ]
    width="<%if msg_editor_width%><%msg_editor_width%><%else%>700<%endif%>"<%endif%>> + + + <%include editor_objects.html%> +
    + + +
    Subscribe To List: + +
    Subscribe To Text: + +
    + + +
    Unsubscribe To List: + +
    Unsubscribe To Text: + +
    + + +
    Move Subscription To Lists: + +
    + + +
    Move Subscription To Text: + +
    + + +
    + checked<%endif%>> Track number of users who open this message.
    + checked<%endif%>> Track click rates. +
    +<%include spellcheck_objects.html%> + + + diff --git a/site/glist/templates/gossamer/msg_include_results.html b/site/glist/templates/gossamer/msg_include_results.html new file mode 100644 index 0000000..6e460ce --- /dev/null +++ b/site/glist/templates/gossamer/msg_include_results.html @@ -0,0 +1,92 @@ + + + + + + + + + + +
    + + + + + + + + + <%loop results%> + + + + + + + + <%endloop%> +
    + Subject + <%if sb eq 'msg_subject'%> + + <%endif%> + + Created + <%ifnot sb%> + + <%endif%> + + +
    + + + <%GList::HTML::generate_attachments%> + <%if attachments%><%else%> <%endif%> + + <%if msg_user_id_fk and msg_user_id_fk ne $usr_username%> + <%set user_search = 1%> + <%elsif msg_user_id_fk eq $usr_username and msg_user_id_fk-opt eq '<>'%> + <%set user_search = 1%> + <%endif%> + <%msg_subject%> + + <%GList::HTML::date_get('msg_created', 1)%> + + + +
    +
    + + <%if hits > $mh and $mh != -1%> + + + + <%endif%> + + + <%if hits%> + + <%endif%> + +
    <%include include_toolbar.html%>
    <%if msg%><%msg%><%else%> <%endif%> + <%if hits_cat%> + Move to: + + + <%endif%> + + +
    +
    + diff --git a/site/glist/templates/gossamer/msg_modify_form.html b/site/glist/templates/gossamer/msg_modify_form.html new file mode 100644 index 0000000..8f8bf6c --- /dev/null +++ b/site/glist/templates/gossamer/msg_modify_form.html @@ -0,0 +1,124 @@ +<%set book_mark = 'modify_message'%> + +<%set set_focus = 'myform.msg_subject'%> +
    +<%include include_header.html%> + + + + + + + + + + + +
    <%include msg_include_menu.html%>
    <%include msg_include_nav.html%> + + + + + + + +
    + + + + +
    + + + + + +
    Modify Message + <%set iclose = 1%> + <%set idetails = 1%> + <%set ipreview = 1%> + <%set isend = 1%> + <%set modify = 1%> + <%include msg_include_icons.html%> +
    +
    + + + + + <%if msg%><%endif%> + + + + + + +
     
    <%msg%>

    + + <%include msg_include_record.html%> +
    + + +
    +
    +
    + +<%hidden_objects%> + +<%include include_footer.html%> +
    diff --git a/site/glist/templates/gossamer/msg_search_form.html b/site/glist/templates/gossamer/msg_search_form.html new file mode 100644 index 0000000..70fa7db --- /dev/null +++ b/site/glist/templates/gossamer/msg_search_form.html @@ -0,0 +1,154 @@ +<%set set_focus = 'myform.msg_id'%> +<%set book_mark = 'search_message'%> +<%include include_header.html%> + + + + + + + + + + + +
    <%include msg_include_menu.html%>
    <%include msg_include_nav.html%> + + + + + + + +
    + + + + +
    + + + + + +
    Find Message + <%set iclose = 1%> + <%include msg_include_icons.html%> +
    +
    +
    + + <%if msg%><%endif%> + + + +

      <%msg%>
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%if usr_type%> + + + + + <%endif%> + + + + + + + + + + + + + + + + + + + +

    • Search Info
    • Message ID + + + + + <%if hits%>  Folder +   + <%endif%> +
      Subject
      From Name
      From Email
      Created + + + + + +
      From:
       To:
        <%GList::language('SYS_DATE_FORMAT')%>
      +
      Message TEXT
      Message HTML
      Username + + +
    • Search Options
    • Keyword search
      Maximum hitsMatch Any
      Sort by + + +
      + + +
      + + <%hidden_objects%> + +
      +
      +
      +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/msg_send_form.html b/site/glist/templates/gossamer/msg_send_form.html new file mode 100644 index 0000000..5d910e1 --- /dev/null +++ b/site/glist/templates/gossamer/msg_send_form.html @@ -0,0 +1,120 @@ +<%set book_mark = 'send_message'%> +<%include include_header.html%> + + + + + + + + + + + +
      <%include msg_include_menu.html%>
      <%include msg_include_nav.html%> +
      + + + + + + + +
      + + + + +
      + + + + + +
      Select Lists + <%set iclose = 1%> + <%set ipreview = 1%> + <%set idetails = 1%> + <%set iedit = 1%> + <%set modify = 1%> + <%include msg_include_icons.html%> +
      +
      + + + + + + + + + + + +
      +
      Selected messages: +
      + <%loop loop_messages%> + + - <%msg_subject%>
      + <%endloop%> +
      +
      + <%if msg%><%msg%><%else%>Send message(s) to the following lists:
      <%endif%> +
      + <%if mh != -1 and hits > $mh%><%include include_toolbar.html%>All<%endif%> +
      + <%if usr_type == 1%> + + <%else%> <%endif%> +
      + + + + +
      + + + + + + + <%loop results%> + + + + + + <%endloop%> +
      List NameSubscribers + +
      <%lst_title%><%if val_subs%><%val_subs%><%else%>0<%endif%> + <%if val_subs%> + + + <%else%>  + <%endif%> +
      +
      + + <%if hits > $mh%> + + + + <%endif%> + + + +
      + <%if mh != -1%><%include include_toolbar.html%>All<%endif%> +
      + + +
      +
      +
      + + <%hidden_objects%> +
      +
      +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/plugin_help.html b/site/glist/templates/gossamer/plugin_help.html new file mode 100644 index 0000000..e88bf09 --- /dev/null +++ b/site/glist/templates/gossamer/plugin_help.html @@ -0,0 +1,45 @@ +<%include include_header.html%> + + + + + + + + + +
      <%include plugin_include_menu.html%>
      + + + + + + + +
      + + + + + +
      Plugins Help


      + + + + +
      +

      Plugins are a powerful new + feature of GList. They allow you to easily extend, enhance or alter + the functionality of any aspect of GList. From your plugin manager, + you can easily install new plugins, check out Gossamer Threads to view + the latest plugins, or update existing ones, remove plugins that are no + longer useful and even temporairly disable/enable installed plugins.

      +

      Included in every copy is + also a powerful plugin authoring tool. It allows people with a little + perl knowledge to easily create plugins, and also share them with the + GList community instantly. Be sure to read the GList developers + guide for more information on enhancing GList.

      +
      +
      +
      +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/plugin_include_menu.html b/site/glist/templates/gossamer/plugin_include_menu.html new file mode 100644 index 0000000..0dee0a9 --- /dev/null +++ b/site/glist/templates/gossamer/plugin_include_menu.html @@ -0,0 +1,31 @@ + + + + + + + + + + + + +
      +   + Plugin Manager + + + Plugin Download + + + Plugin Wizard + + + About Plugins + + + GT Module Documentation + + +
      + diff --git a/site/glist/templates/gossamer/plugin_manager.html b/site/glist/templates/gossamer/plugin_manager.html new file mode 100644 index 0000000..7059064 --- /dev/null +++ b/site/glist/templates/gossamer/plugin_manager.html @@ -0,0 +1,49 @@ +<%include include_header.html%> + + + + + + + + + +
      <%include plugin_include_menu.html%>
      + + + + + + + +
      + + + + + +
      <%if gossamer%>Install Plugins from Gossamer<%else%>Plugin Manager<%endif%>
      + <%GList::Plugins::admin_menu%> + + + <%if menu%> + + <%endif%> + + +
      + <%menu%>
      +

      + <%if gossamer%> + From here you can download plugins straight from Gossamer to your server. + <%else%> + <%if msg%><%msg%><%else%>The plugin manager lets you install, edit and remove plugins on your system.<%endif%>

      + <%GList::Plugins::manager%> + <%if error%><%error%><%endif%> + <%if content%><%content%><%endif%> + <%endif%> +
      +
      +
      +
      +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/plugin_manager_delete.html b/site/glist/templates/gossamer/plugin_manager_delete.html new file mode 100644 index 0000000..c71a182 --- /dev/null +++ b/site/glist/templates/gossamer/plugin_manager_delete.html @@ -0,0 +1,26 @@ +
      + + + + + + + + + +

      +

      About to delete plugin: <%plugin_name%> permanently!

      +

      This will remove the plugin from your system completely. Are you sure:

      + + + + +
      +    + +

      +

      +<%if sid%> + +<%endif%> +
      diff --git a/site/glist/templates/gossamer/plugin_manager_download.html b/site/glist/templates/gossamer/plugin_manager_download.html new file mode 100644 index 0000000..132fc44 --- /dev/null +++ b/site/glist/templates/gossamer/plugin_manager_download.html @@ -0,0 +1,112 @@ +<%set book_mark = 'download'%> +<%if gossamer%> +
      + + + + + + + +
      + <%if results%> +

      <%results%>

      + <%endif%> + <%if error%> +

      <%error%>

      + <%endif%> + <%gossamer%> +
      + <%hidden_objects%> +
      +<%else%> + <%include include_header.html%> + + + + + + + + + +
      <%include plugin_include_menu.html%>
      + + + + + + + +
      + + + + + +
      Add New Plugin

      + + + + + + +
      +
        + You can add new plugins either by uploading a file, downloading from a URL, or downloading straight from Gossamer Threads. +
      +
      + + + + +
      + + + + + + + + + + + + + +
    • List Plugins on Gossamer Threads

    • + <%if results%> +

      <%results%>

      + <%endif%> + <%if error%> +

      <%error%>

      + <%endif%> +
      + + + + + <%hidden_objects%> +
      +
    • Install From
    • +
      + + + +

      URL:

      + <%hidden_objects%> +
      +
      + + + +

      File:

      + <%hidden_objects%> +
      +
      +
      +
      +
      +
      + <%include include_footer.html%> +<%endif%> diff --git a/site/glist/templates/gossamer/plugin_manager_edit.html b/site/glist/templates/gossamer/plugin_manager_edit.html new file mode 100644 index 0000000..ef1d817 --- /dev/null +++ b/site/glist/templates/gossamer/plugin_manager_edit.html @@ -0,0 +1,89 @@ +<%if results%> +

      <%results%>

      +<%endif%> +<%if error%> +

      <%error%>

      +<%endif%> + +
      + + + + + +<%if sid%> + +<%endif%> +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%options%> + <%menu%> + <%hooks%> +
      Plugin Details +
       
      + Plugin: + + <%plugin_name%> +
      + Version: + + <%version%>1 +
      + Author: + + <%author%> +
      + License: + + <%license%> +
      + Description: + + <%description%> +
       
      +
      +
      +
      + + + + +
      +
      +
      +<%if reload%> + +<%endif%> diff --git a/site/glist/templates/gossamer/plugin_manager_edit_files.html b/site/glist/templates/gossamer/plugin_manager_edit_files.html new file mode 100644 index 0000000..91f2f95 --- /dev/null +++ b/site/glist/templates/gossamer/plugin_manager_edit_files.html @@ -0,0 +1,101 @@ +<%if results%> +

      <%results%>

      +<%endif%> +<%if error%> +

      <%error%>

      +<%endif%> + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + <%if files%> + + + + <%endif%> + <%if body%> + + + + + + + + + + <%else%> + + + + + + + + + + <%endif%> +
    • Plugin Details
    • Plugin:<%plugin_name%>
      Version:<%if version%><%version%><%endif%>
      Author:<%if author%><%author%><%endif%>
      License:<%if license%><%license%><%endif%>
      Description:<%description%>
       
    • Plugin Files
    • <%files%>
       
    • Edit <%body_name%>:
    • +
      + + + + + + +
      +
       
    • Add New File:
    • + + + + + + + + + + + + + + +
      Upload File:
      File Name:
      +
      +
      +

      +

      +<%if sid%> + +<%endif%> + diff --git a/site/glist/templates/gossamer/plugin_manager_list.html b/site/glist/templates/gossamer/plugin_manager_list.html new file mode 100644 index 0000000..d97c0d6 --- /dev/null +++ b/site/glist/templates/gossamer/plugin_manager_list.html @@ -0,0 +1,21 @@ +<%if results%> +

      <%results%>

      +<%endif%> +<%if error%> +

      <%error%>

      +<%endif%> + +<%if installed%> +

      <%installed%>

      +<%endif%> +<%if uninstalled%> +

      <%uninstalled%>

      +<%endif%> +<%if reload%> + +<%endif%> + diff --git a/site/glist/templates/gossamer/plugin_manager_pre_install.html b/site/glist/templates/gossamer/plugin_manager_pre_install.html new file mode 100644 index 0000000..a74ece5 --- /dev/null +++ b/site/glist/templates/gossamer/plugin_manager_pre_install.html @@ -0,0 +1,38 @@ +
      + + + + +<%if sid%> + +<%endif%> +

      About to install plugin: <%plugin_name%>

      + +<%if error%> +

      Error loading plugin: <%error%>

      +<%endif%> + +<%if instructions%> +

      Please read the following instructions from the <%plugin_name%> author: +

      + <%instructions%> +

      +<%endif%> + +

      + +<%if confirm%> + +

      Plugin Exists!

      +

      <%plugin_name%> plugin already exists. Do you want to overwrite version <%old_version%> with <%new_version%>:

      +    +    + +

      +<%endif%> +<%ifnot confirm%> +
      +
      +
      +<%endif%> + diff --git a/site/glist/templates/gossamer/plugin_manager_pre_uninstall.html b/site/glist/templates/gossamer/plugin_manager_pre_uninstall.html new file mode 100644 index 0000000..a77faf4 --- /dev/null +++ b/site/glist/templates/gossamer/plugin_manager_pre_uninstall.html @@ -0,0 +1,30 @@ +
      + + + + +

      About to remove plugin: <%plugin_name%>

      + +<%if error%> +

      Error loading plugin: <%error%>

      +<%endif%> + +<%if instructions%> +

      Please read the following instructions from the <%plugin_name%> author: +

      + <%instructions%> +

      +<%endif%> + +

      + +
      +
      +    + +
      +
      +<%if sid%> + +<%endif%> +
      diff --git a/site/glist/templates/gossamer/plugin_wizard.html b/site/glist/templates/gossamer/plugin_wizard.html new file mode 100644 index 0000000..609155e --- /dev/null +++ b/site/glist/templates/gossamer/plugin_wizard.html @@ -0,0 +1,50 @@ +<%GList::Plugins::wizard%> +<%set book_mark = 'wizard'%> +<%include include_header.html%> + + + + + + + + + +
      <%include plugin_include_menu.html%>
      + + + + + + + +
      + + + + + +
      + Plugin Wizard - + <%ifnot plugin_wiz_do%>Step 1: Name your plugin + <%elsif plugin_wiz_do eq 'step2'%> Step 2: Meta Information for <%plugin_name%> + <%elsif plugin_wiz_do eq 'step3'%> Step 3: Plugin Hooks for <%plugin_name%> + <%elsif plugin_wiz_do eq 'step4'%> Step 4: Plugin Admin Menu for <%plugin_name%> + <%elsif plugin_wiz_do eq 'step5'%> Step 5: Plugin User Options for <%plugin_name%> + <%elsif plugin_wiz_do eq 'step6'%> Step 6: Included Files for <%plugin_name%> + <%elsif plugin_wiz_do eq 'step7'%> Step 7: Install Information for <%plugin_name%> + <%elsif plugin_wiz_do eq 'create'%> Step 8: All Finished! + <%endif%> +
      +

      +

        This wizard will walk you through some questions and create a plugin shell for you to use.

      + + + + +
      + <%content%> +
      +
      +
      +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/plugin_wizard_step1.html b/site/glist/templates/gossamer/plugin_wizard_step1.html new file mode 100644 index 0000000..8212157 --- /dev/null +++ b/site/glist/templates/gossamer/plugin_wizard_step1.html @@ -0,0 +1,53 @@ + + + + +
      + + + + +
      + Welcome to the plugin wizard. This wizard will ask you a few questions about your plugin, and then create an install template and code + template which you can edit later.

      + To begin with, we need to know the name of your plugin. The name + will be used to determine the package the plugin resides in, so it + must be a valid perl package space. You should start with capitals, + and capitlize words as in: SearchLogger or SearchCache. + <%if error%> +

      <%error%>

      + <%endif%> + + + + + + <%if edit%> + + + + + <%endif%> +
      Create new plugin named: +
      + + + + + <%if sid%> + + <%endif%> +
      +
      Use existing plugin: +
      + + + + <%edit%> + <%if sid%> + + <%endif%> +
      +
      +
      +
      diff --git a/site/glist/templates/gossamer/plugin_wizard_step2.html b/site/glist/templates/gossamer/plugin_wizard_step2.html new file mode 100644 index 0000000..8cd6d81 --- /dev/null +++ b/site/glist/templates/gossamer/plugin_wizard_step2.html @@ -0,0 +1,88 @@ +
      + + + + +<%if sid%> + +<%endif%> + + + + +
      + + + + +
      + Now we need to know a few more details about your plugin. This will be + used in displaying information about your plugin such as author, + description and most importantly version number. +

      It's essential to keep + a version number for your plugin. When people update the plugin from + the web, they can easily tell if the version they have is older then + the latest version, and can update as required.

      + <%if error%> +

      <%error%>

      + <%endif%> + + + + + + + + + + + + + + + + + + + + + + + + + +
      Version: +

      +
      Author: + +
      URL: + +
      License: + +
      MLists Version Required: + +
      Description: + +
      +
      +

      + + + + +
      + + + + +
      + +
      +
      +
      diff --git a/site/glist/templates/gossamer/plugin_wizard_step3.html b/site/glist/templates/gossamer/plugin_wizard_step3.html new file mode 100644 index 0000000..dc89053 --- /dev/null +++ b/site/glist/templates/gossamer/plugin_wizard_step3.html @@ -0,0 +1,80 @@ + + + + +
      + + + + +
      +
      + + + + + Now you need to decide what hooks, if any, the plugin will use. +

      If your plugin needs + to override or enhance an existing part of code, then you most likely + need to register some hooks. If you are adding new functionality, then + you may just skip this section entirely.

      + <%if error%> +

      <%error%>

      + <%endif%> + <%if results%> +

      <%results%>

      + <%endif%> + <%if hooks%> +

      Existing Hooks:

      + <%hooks%> +

      +

      + <%endif%> +

      Add New Hook:

      + + + + + + + + + + + + + +
      Name
      Type + +
      Code + +
      +

      +

      +
      +
      +
      + + + + + <%if sid%> + + <%endif%> + + + + +
      + + + + +
      + +
      +
      +
      diff --git a/site/glist/templates/gossamer/plugin_wizard_step4.html b/site/glist/templates/gossamer/plugin_wizard_step4.html new file mode 100644 index 0000000..4556317 --- /dev/null +++ b/site/glist/templates/gossamer/plugin_wizard_step4.html @@ -0,0 +1,72 @@ + + + + + +
      + + + + +
      +
      + + + + + Now you need to decide what menu options, if any, you want to be displayed + to the user. If you provide admin menu options, for the user to interact with + the plugin, you should provide the name you want displayed, and the URL + you want it to go to. +

      To run a function in your code, the URL should look like:

      +    plugin.cgi?do=plugin&plugin=<%plugin_name%>&func=yourfunc

      + This will run the function called yourfunc. You are responsible for printing + headers and any output you want.

      + <%if error%> +

      <%error%>

      + <%endif%> + <%if results%> +

      <%results%>

      + <%endif%> + <%if menu%> +

      Existing Options:

      <%menu%>

      +

      + <%endif%> +

      Add New Menu:

      + + + + + + + + + +
      Name
      URL
      +

      +

      +
      +
      + +
      + + + + +<%if sid%> + +<%endif%> + + + + +
      + + + + +
      + +
      +
      +
      diff --git a/site/glist/templates/gossamer/plugin_wizard_step5.html b/site/glist/templates/gossamer/plugin_wizard_step5.html new file mode 100644 index 0000000..e401d53 --- /dev/null +++ b/site/glist/templates/gossamer/plugin_wizard_step5.html @@ -0,0 +1,73 @@ + + + + +
      + + + + +
      +
      + + + + + Now you need to decide what user options, if any, you want to allow the user + to change. You can set simple configuration variables here that will be available + to you inside of your plugin. Links SQL will provide an interface for the user + to change these variables and provide the mechanisim to store them. You can + also provide instructions which can explain to the user what the option + is for. + <%if error%> +

      <%error%>

      + <%endif%> + <%if results%> +

      <%results%>

      + <%endif%> + <%if user%> +

      Existing Options:

      <%user%>

      +

      + <%endif%> +

      Add User Option:

      + + + + + + + + + + + + + +
      Name
      Value
      Instructions
      +

      +

      +
      +
      +
      +
      + + + + +<%if sid%> + +<%endif%> + + + + +
      + + + + +
      +
      +
      +
      +
      diff --git a/site/glist/templates/gossamer/plugin_wizard_step6.html b/site/glist/templates/gossamer/plugin_wizard_step6.html new file mode 100644 index 0000000..23eb14a --- /dev/null +++ b/site/glist/templates/gossamer/plugin_wizard_step6.html @@ -0,0 +1,84 @@ + + + + + +
      + + + + +
      +
      + + + + +<%if sid%> + +<%endif%> + Quite often you will need to bundle some extra files with your plugin, be it + a user cgi script, some images, a library file, or whatever. From here you + can add files to be included in your plugin, and where you would like them + placed. + +<%if error%> +

      <%error%>

      +<%endif%> +<%if results%> +

      <%results%>

      +<%endif%> +<%if files%> +

      Existing Files:

      +<%files%> +

      +

      +<%endif%> +

      Add New File:

      + + + + + + + + + + + + + + + + + + + + +
      Filename
      Place Into +
      File
      or
      Body
      +

      +

      + +
      + +

      + +
      + + + + +<%if sid%> + +<%endif%> + +
      +
      +
      +
      diff --git a/site/glist/templates/gossamer/plugin_wizard_step7.html b/site/glist/templates/gossamer/plugin_wizard_step7.html new file mode 100644 index 0000000..6916874 --- /dev/null +++ b/site/glist/templates/gossamer/plugin_wizard_step7.html @@ -0,0 +1,53 @@ +
      + + + + +<%if sid%> + +<%endif%> + + + + + +
      + + + + +
      MList provides the plugin author to display a message before your plugin is +installed, and before it is removed. You should take this chance to provide some +feedback to the user on what actions you are going to do, and what changes the +plugin will make. + +<%if error%> +

      <%error%>

      +<%endif%> + + + + + + + + + + + + + + + + + +
      Install Message
      UnInstall Message
      Install Code
      Uninstall Code
      +
      +
      + +

      + +
      +
      +
      +
      diff --git a/site/glist/templates/gossamer/plugin_wizard_step8.html b/site/glist/templates/gossamer/plugin_wizard_step8.html new file mode 100644 index 0000000..a3cef2c --- /dev/null +++ b/site/glist/templates/gossamer/plugin_wizard_step8.html @@ -0,0 +1,35 @@ +
      + + + + +<%if sid%> + +<%endif%> + + + + + +
      + + + + +
      +Your +plugin file has been created, and an install file has been added to it. There is +also a Wizard.pm file, so if you ever need to re run the wizard, all your values +will be remembered. +

      You should now go to the Plugin +Editor and add your source code to make your plugin work!

      +
      +
      + +

      + +
      +
      +
      + +
      diff --git a/site/glist/templates/gossamer/pro_include_menu.html b/site/glist/templates/gossamer/pro_include_menu.html new file mode 100644 index 0000000..7667c35 --- /dev/null +++ b/site/glist/templates/gossamer/pro_include_menu.html @@ -0,0 +1,12 @@ + + + + + + + + + + + +
       AccountChange PasswordEmail TemplatesHeader & Footer
      diff --git a/site/glist/templates/gossamer/pro_include_record.html b/site/glist/templates/gossamer/pro_include_record.html new file mode 100644 index 0000000..bec3f8b --- /dev/null +++ b/site/glist/templates/gossamer/pro_include_record.html @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + + + + <%if usr_type == 2%> + + + + + + + + + + + + + <%elsif usr_type == 3%> + + + + + + + <%endif%> + + + + <%-- + This auto generates the profile form, so if you add profile options it + will automatically appear. + --%> + <%GList::GUI::gui_profile_form( + td_l => 'class="body" align="left"', + td_r => 'class="body" align="left"', + required => '*', + prefix => 'mod_' + )%> + <%-- + Now add any extra application fields needed. + --%> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    • Account Infomation
      • Username
      <%mod_usr_username%>
        Email address
      + * +
        Reply-to Email
      + +
        Bounce Email
      + +
    • Account Limits
      • Number of list: <%usr_limit_list%>
        Number of subscriber per list: <%usr_limit_sublist%>
        Number of email in the last 30 days: <%usr_limit_email30%>
    • Account Limits
      • Unlimited account
    • Profile Infomation
    • Default Values
      • Compose Mode
      + +
        Advanced Editor
      + +
        Date format
      +
      + +

        The pop settings below are for bounced emails.
        Server mail (POP3)
      + +
        Port
      + +
        Account Name
      + 35<%else%>25<%endif%> value="<%if mod_usr_mail_account%><%mod_usr_mail_account%><%endif%>" /> +
        Account Password
      + 35<%else%>25<%endif%> value="<%if mod_usr_mail_password%><%mod_usr_mail_password%><%endif%>" /> +
      (*): Required
      diff --git a/site/glist/templates/gossamer/pro_include_report_details.html b/site/glist/templates/gossamer/pro_include_report_details.html new file mode 100644 index 0000000..e0e35e7 --- /dev/null +++ b/site/glist/templates/gossamer/pro_include_report_details.html @@ -0,0 +1,51 @@ + + + + + + + +
      + + + + + + + + <%set logged_user = $usr_email%> + <%loop results%> + + + + + + + <%endloop%> +
      + ID + <%if sb eq 'mli_id' or not $sb%> + + <%endif%> + + Subject + <%if sb eq 'mli_subject'%> + + <%endif%> + + Sent + <%if sb eq 'mli_done'%> + + <%endif%> + Total Recipients
      <%mli_id%><%mli_subject%><%GList::HTML::date_get('mli_done', 1)%><%sent%>
      +
      +<%if hits%> + + + <%if hits > $mh%> + + <%endif%> + + +
      <%include include_toolbar.html%> $mh%>"2"<%else%>4<%endif%> align=right class=body>Total Recipients: <%if total_recipients%><%total_recipients%><%endif%> 
      +<%endif%> diff --git a/site/glist/templates/gossamer/pro_include_report_form.html b/site/glist/templates/gossamer/pro_include_report_form.html new file mode 100644 index 0000000..ef664f7 --- /dev/null +++ b/site/glist/templates/gossamer/pro_include_report_form.html @@ -0,0 +1,46 @@ +
      + + <%if usr_type == 1%> + + + + + <%endif%> + + + + +
      Username + +
      Month + + + + +
      + - <%GList::HTML::generate_years%> + + + [ Advanced ] +
      +
      + diff --git a/site/glist/templates/gossamer/pro_include_report_menu.html b/site/glist/templates/gossamer/pro_include_report_menu.html new file mode 100644 index 0000000..844c9c5 --- /dev/null +++ b/site/glist/templates/gossamer/pro_include_report_menu.html @@ -0,0 +1,19 @@ + + + + + + + + + +
      + Reports + + Help + <%if usr_type and d%> + + <%elsif do eq 'rpt_form'%> + + <%endif%> +
      diff --git a/site/glist/templates/gossamer/pro_include_report_summary.html b/site/glist/templates/gossamer/pro_include_report_summary.html new file mode 100644 index 0000000..4bc2491 --- /dev/null +++ b/site/glist/templates/gossamer/pro_include_report_summary.html @@ -0,0 +1,58 @@ + + + + + + + +
      + + + + + + + + <%set logged_user = $usr_username%> + <%loop results%> + + + + + + + <%endloop%> +
      + Username + <%if sb eq 'usr_username' or $sb eq ''%> + + <%endif%> + + Email + <%if sb eq 'usr_email'%> + + <%endif%> + + Full Name + <%if sb eq 'pro_first_name'%> + + <%endif%> + Total Mailings
      + + <%if usr_username eq $logged_user%><%endif%> + <%if sent%><%usr_username%><%else%><%usr_username%><%endif%> + + <%if usr_username eq $logged_user%><%endif%><%usr_email%> + <%if usr_username eq $logged_user%><%endif%><%pro_first_name%> <%pro_last_name%><%if usr_email eq $logged_user%><%endif%> + <%if sent%><%sent%><%else%>0<%endif%> +
      +
      +<%if hits%> + + <%if hits > $mh%> + + + + <%endif%> +
      <%include include_toolbar.html%>
      +<%endif%> diff --git a/site/glist/templates/gossamer/pro_mailing.html b/site/glist/templates/gossamer/pro_mailing.html new file mode 100644 index 0000000..cb3bc4b --- /dev/null +++ b/site/glist/templates/gossamer/pro_mailing.html @@ -0,0 +1,77 @@ +<%set set_focus = 'myform.mod_usr_header_html'%> +<%set book_mark = 'profile'%> +<%include include_header.html%> + + + + + + + + + +
      <%include pro_include_menu.html%>
      + + + + + + + +
      + + + + + + + +
      + + + + + +
      Mailing Header & Footer
      +
      +
      + <%if msg%>
      <%msg%>
      <%endif%> +
      + + + + + + + +
      + + + + + + + + + + + + + + + + + +
      HTML Header
      Text Header
      HTML Footer
      Text Footer
      +

      + + +

      + + + <%hidden_objects%> +
      +
      +
      +
      +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/pro_password_form.html b/site/glist/templates/gossamer/pro_password_form.html new file mode 100644 index 0000000..6da79f5 --- /dev/null +++ b/site/glist/templates/gossamer/pro_password_form.html @@ -0,0 +1,55 @@ +<%set set_focus = 'myform.old_pass'%> +<%set book_mark = 'password'%> +<%include include_header.html%> + + + + + + + + + +
      <%include pro_include_menu.html%>
      +
      + + + + + + + +
      + + + + + +
      Change Password
      +
      + <%if msg%>

      <%msg%>

      <%endif%>
      + + + + + + + + + + + + + + + + +
      Old Password
      New Password
      Re-type new

      + +
      +
      + + <%hidden_objects%> +
      +
      +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/pro_profile.html b/site/glist/templates/gossamer/pro_profile.html new file mode 100644 index 0000000..dbd1c8f --- /dev/null +++ b/site/glist/templates/gossamer/pro_profile.html @@ -0,0 +1,57 @@ +<%set set_focus = 'myform.mod_pro_first_name'%> +<%set book_mark = 'profile'%> +<%include include_header.html%> + + + + + + + + + +
      <%include pro_include_menu.html%>
      + + + + + + + +
      + + + + + + + +
      + + + + + +
      Update Your Profile
      +
      +
      + <%if msg%>
      <%msg%>
      <%endif%> +
      + + + + + + + +
      <%include pro_include_record.html%>

      + + +

      + + <%hidden_objects%> +
      +
      +
      +
      +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/pro_report.html b/site/glist/templates/gossamer/pro_report.html new file mode 100644 index 0000000..1733a67 --- /dev/null +++ b/site/glist/templates/gossamer/pro_report.html @@ -0,0 +1,40 @@ +<%if usr_type%><%set set_focus = 'myform.id'%><%endif%> +<%include include_header.html%> + + + + + + + + + +
      <%include pro_include_report_menu.html%>
      + + + + +
      +
      + <%include pro_include_report_form.html%> + <%if msg%>

      <%msg%><%endif%> + + + + +
      + <%ifnot usr_type%> + <%include pro_include_report_details.html%> + <%elsif d%> + <%include pro_include_report_details.html%> + <%else%> + <%include pro_include_report_summary.html%> + <%endif%> +
      + + + <%hidden_objects%> +

      +
      +
      +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/pro_report_form.html b/site/glist/templates/gossamer/pro_report_form.html new file mode 100644 index 0000000..2610b9e --- /dev/null +++ b/site/glist/templates/gossamer/pro_report_form.html @@ -0,0 +1,59 @@ +<%if usr_type%><%set set_focus = 'myform.id'%><%endif%> +<%include include_header.html%> + + + + + + + + + +
      <%include pro_include_report_menu.html%>
      + + + + +
      +
      + + + + +

      <%if msg%><%msg%>
      <%endif%>
      + + <%if usr_type == 1%> + + + + + <%endif%> + + + + + + + + + + +
      Username + +
      From + + To: + + <%GList::language('SYS_DATE_FORMAT')%> +
      Max Hits + + + +
      +
      + + <%hidden_objects%> +
      +
      +
      +<%include include_footer.html%> diff --git a/site/glist/templates/gossamer/pro_template.html b/site/glist/templates/gossamer/pro_template.html new file mode 100644 index 0000000..0a077d7 --- /dev/null +++ b/site/glist/templates/gossamer/pro_template.html @@ -0,0 +1,111 @@ +<%set set_focus = 'myform.tpl_to'%> +<%set book_mark = 'template'%> + +<%include include_header.html%> + + + + + + + + + +
      <%include pro_include_menu.html%>
      + + + + + + + +
      + + + + + +
      Email Templates
      +
      + <%GList::Tools::email_editor%> +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Templates + <%if current_list%> + <%current_list%> + + <%if tpl_name%> + + + <%endif%> + <%else%> + + <%endif%> +
      To + * +
      Subject + * +
      From + * +
      Extra Headers + +
      E-mail body * + <%if msg%><%msg%><%endif%> +
      + +
      + Save template as + + +
      + + + + +
      + *: required +
      + + <%hidden_objects%> +
      +
      +
      +<%include include_footer.html%> + diff --git a/site/glist/templates/gossamer/user_login.html b/site/glist/templates/gossamer/user_login.html new file mode 100644 index 0000000..4ae5f02 --- /dev/null +++ b/site/glist/templates/gossamer/user_login.html @@ -0,0 +1,66 @@ + + + <%if site_title%><%site_title%><%else%>Gossamer Gossamer List<%endif%> - Login + <%include include_style.txt%> + +<%set set_focus = 'myform.username'%> +onload="document.<%set_focus%>.focus()"<%endif%>> +
      + + + + +
      + + + + + + + +
      + + + + + + + +
      + + + + + +
      Login
      +

      +

      <%msg%>

      + + + + + + + + + + + + + +
      Username:
      Password:
        + +         + Lost Password? +

      + <%if signup_enable%> +  New to Gossamer List? Sign up now! + <%endif%> +
      +
      +
      + + +
      + + diff --git a/site/glist/templates/gossamer/user_remind_form.html b/site/glist/templates/gossamer/user_remind_form.html new file mode 100644 index 0000000..34cd637 --- /dev/null +++ b/site/glist/templates/gossamer/user_remind_form.html @@ -0,0 +1,53 @@ + + + <%if site_title%><%site_title%><%else%>Gossamer Mailing Lists<%endif%> - Login + <%include include_style.txt%> + + +
      + + + + +
      + + + + + + + +
      + + + + + + + +
      + + + + + +
      Remind Me
      +

      + + + + + + + +
      <%if msg%><%msg%><%else%>Forgot your Password?
      Enter your email address to retrieve your lost password. <%endif%>

      + +
      +

      +
      +
      +
      + +
      + + diff --git a/site/glist/templates/gossamer/user_signup.html b/site/glist/templates/gossamer/user_signup.html new file mode 100644 index 0000000..45aa97a --- /dev/null +++ b/site/glist/templates/gossamer/user_signup.html @@ -0,0 +1,91 @@ + + + <%if site_title%><%site_title%><%else%>Gossamer Gossamer List<%endif%> - Login + <%include include_style.txt%> + +<%set set_focus = 'myform.usr_username'%> +onload="document.<%set_focus%>.focus()"<%endif%>> +
      + + + + +
      + + + + + + + +
      + + + + + + + + + + + +
      + + + + + +
      Sign up for an account
      +

      +

      <%if msg%>

      <%msg%>
      <%endif%>

      + + + + + + + + + + + + + + + + + + + + + + + + <%-- + This auto generates the profile form, so if you add profile options it + will automatically appear. + --%> + <%GList::GUI::gui_profile_form( + td_l => 'class="body" align="right"', + td_r => 'class="body" align="left"', + required => '*' + )%> + <%-- + Now add any extra application fields needed. + --%> + + + +
    • Account Infomation
    • Username:*
      Password:
      *
      Confirm Password:
      *
      Email Address:
      *
    • Profile Infomation

    • (*): Required
      +
       Sign In + +   +
      +
      +
      + +<%hidden_objects%> +
      + + diff --git a/site/glist/templates/gossamer/user_subscribe.html b/site/glist/templates/gossamer/user_subscribe.html new file mode 100644 index 0000000..2c911cd --- /dev/null +++ b/site/glist/templates/gossamer/user_subscribe.html @@ -0,0 +1,142 @@ + + + <%if site_title%><%site_title%><%else%>Gossamer Gossamer List<%endif%> - Subscribe + <%include include_style.txt%> + +<%set set_focus = 'myform.usr_username'%> +onload="document.<%set_focus%>.focus()"<%endif%>> +
      + + + + +
      + + + + + + + +
      + + + + + + + + + + + +
      + + + + + +
      Subscribe to a Mailing List
      +

      +

      <%if msg%>

      <%msg%>
      <%endif%>

      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      Name:*
      Email Address:
      *
      Company Name:
      Website Address:
      Shipping Address:
      Race Director Name:
      Race Director Phone:
      Race Director Email:
      Sponsorship coordinator Name:
      Sponsorship coordinator Phone:
      Sponsorship coordinator Email:
      Press Relation Manager Name:
      Press Relation Manager Phone:
      Press Relation Manager Email:
      Pro Athlete Liaison Name:
      Pro Athlete Liaison Phone:
      Pro Athlete Liaison Email:
      Goods & Services Buyer Name:
      Goods & Services Buyer Phone:
      Goods & Services Buyer Email:

      (*): Required
      +
       Sign In + +
      +
      +
      + + +<%hidden_objects%> +
      + + diff --git a/site/glist/templates/gossamer/user_subscribe_success.html b/site/glist/templates/gossamer/user_subscribe_success.html new file mode 100644 index 0000000..2f6b784 --- /dev/null +++ b/site/glist/templates/gossamer/user_subscribe_success.html @@ -0,0 +1,40 @@ + + + <%if site_title%><%site_title%><%else%>Gossamer List<%endif%> - Subscribe + <%include include_style.txt%> + +onload="document.<%set_focus%>.focus()"<%endif%>> + + + + +
      + + + + + + + +
      + + + + + + + +
      + + + + + +
      Subscribe to a Mailing List
      +

      + Thank you, you are successfully subscribed! +
      +
      +
      + + diff --git a/site/glist/templates/gossamer/user_success_form.html b/site/glist/templates/gossamer/user_success_form.html new file mode 100644 index 0000000..ba12eab --- /dev/null +++ b/site/glist/templates/gossamer/user_success_form.html @@ -0,0 +1,27 @@ + + + <%if site_title%><%site_title%><%else%>Gossamer Mailing Lists<%endif%> - Login + <%include include_style.txt%> + + + + + + +
      + + + + + + +
      Gossamer List

      + + + + +
      <%msg%>
      +
      +
      + + diff --git a/site/glist/templates/help/GT/AutoLoader.html b/site/glist/templates/help/GT/AutoLoader.html new file mode 100644 index 0000000..be95bde --- /dev/null +++ b/site/glist/templates/help/GT/AutoLoader.html @@ -0,0 +1,467 @@ + + + +GT::AutoLoader - load subroutines on demand + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::AutoLoader - load subroutines on demand

      +

      +

      +
      +

      SYNOPSIS

      +
      +    package GT::Module;
      +    use GT::AutoLoader; # You now have an AUTOLOAD subroutine that will check for entries in %COMPILE
      +

      or

      +
      +    package GT::OtherModule;
      +    use GT::AutoLoader(NAME => '_AUTOLOAD'); # Import AUTOLOAD as _AUTOLOAD, define our own AUTOLOAD
      +    sub AUTOLOAD {
      +        ...
      +        goto &_AUTOLOAD;
      +    }
      +

      then:

      +
      +    $COMPILE{sub} = __LINE__ . <<'END_OF_SUB';
      +    sub method_name {
      +        ...
      +    }
      +    END_OF_SUB
      +

      +

      +
      +

      DESCRIPTION

      +

      The GT::AutoLoader module works as a way to speed up your code. Currently, +the only thing it does is scan for a %COMPILE hash in your package. If it finds +it, it looks for the subroutine you called, and if found compiles and runs it.

      +

      If unable to find a subroutine to compile in %COMPILE, GT::AutoLoader will +scan your inheritance tree (@ISA) for another AUTOLOAD subroutine to pass this +off to. If there isn't any, a fatal error occurs.

      +

      To use GT::AutoLoader, in its standard behaviour, simply put: +use GT::AutoLoader; in your module. When you use GT::AutoLoader, two things +will happen. First, an AUTOLOAD subroutine will be imported into your +namespace that will automatically compile your subroutines only when they are +needed, thus speeding up compile time. Secondly, a %COMPILE hash will be defined +in your package, eliminating the need for you to: use vars qw/%COMPILE/;

      +

      +

      +
      +

      USE

      +

      You can pass options to GT::AutoLoader to change the behaviour of the module. +Currently, logging is the only option, however more options (perhaps including +a different compiling scheme) will be added at some future point.

      +

      Options are specified as import() arguments. For example:

      +
      +    use GT::AutoLoader(OPTION => "value");
      +
      +
      NAME
      +
      +
      +If you want to import the autoload subroutine as something other than +'Package::AUTOLOAD', the 'NAME' option should be used. Its value is the name +to import as. For example, to import a GT::AutoLoader AUTOLOAD named _AUTOLOAD +(this is useful when declaring your own AUTOLOAD behaviour, but still using +GT::AutoLoader's behaviour as a fallback), you would do something like: +
      +
      +
      +    use GT::AutoLoader(NAME => '_AUTOLOAD');
      +
      +

      +
      LOG
      +
      +
      +Takes a code reference as its value. The code reference will be called three +arguments - the package name, the name of the function, and the autoload method +(Currently only 'COMPILE'). Note that this will be called for ALL autoloaded +subroutines, not just the ones in your package. +
      +
      +

      WARNING - you cannot put code in your log that relies on autoloaded methods - +you'll end up throwing the program into an infinite loop.

      +
      +
      +

      For example, to get a line of debugging after each subroutine is compiled, you +could use GT::AutoLoader like this:

      +
      +
      +
      +    use GT::AutoLoader(LOG => sub {
      +        print "Compiled $_[1] in package $_[0]\n"
      +    });
      +
      +

      +
      NEXT
      +
      +
      +Normally, GT::AutoLoader will look for another AUTOLOAD to call in your +package's @ISA inheritance tree. You can alter this behaviour and tell +GT::AutoLoader what to call next using the NEXT option. +
      +
      +

      For example, if you have a sub _AUTOLOAD { } that you wanted to call if the +method isn't found by GT::AutoLoader, you would use GT::AutoLoader like this:

      +
      +
      +
      +    use GT::AutoLoader(NEXT => 'Package::Name::_AUTOLOAD');
      +
      +
      +

      The _AUTOLOAD function in your package will now be called if GT::AutoLoader +can't load the method on its own. $AUTOLOAD will be set for you in whichever +package the function you provide is in. Note that if you simply want to use an +inherited AUTOLOAD, you should not use this option; GT::AutoLoader will +handle that just fine on its own.

      +
      +
      +

      You may omit the package (Package::Name::) if the function is in your current +package.

      +
      +

      +

      +

      +
      +

      compile_all

      +

      A function exists in GT::AutoLoader to compile all %COMPILE-subroutines. By +default (without arguments) compile_all() compiles every %COMPILE-subroutine in +every package that has used GT::AutoLoader. You can, however, pass in a list of +packages which compile_all() will check instead of compiling everything. Note +that GT::AutoLoader will only compile %COMPILE-subroutines in packages that +have used GT::AutoLoader, so if you specify package ``Foo'', but ``Foo'' hasn't +used GT::AutoLoader, it will be ignored.

      +

      You can do something like:

      +
      +    GT::AutoLoader::compile_all(__PACKAGE__) if MOD_PERL;
      +

      to have a GT::AutoLoader compile every %COMPILE-subroutine in the current +package automatically under mod_perl, or you could add this code to your +mod_perl startup file or test script:

      +
      +    GT::AutoLoader::compile_all;
      +

      Test scripts should definately use compile_all() to ensure that all subroutines +compile correctly!

      +

      +

      +
      +

      REQUIREMENTS

      +

      None.

      +

      +

      +
      +

      CAVEATS

      +

      Due to the nature of Perl's AUTOLOAD handling, you must take care when using +GT::AutoLoader in a subclass. In short, subclassed methods MUST NOT be put +into the %COMPILE hash.

      +

      The problem is that since the subroutine does not exist in the package, Perl, +while decending the inheritance tree, will not see it but will probably see the +parent's method (unless nothing else has called the method, but you should +never count on that), and call it rather than looking for your package's +AUTOLOAD.

      +

      This isn't to say that subclasses cannot use AUTOLOAD - just that subclasses +cannot use autoloaded methods (%COMPILE-subroutines) if a method of the same +name exists in the parent class. Autoloaded function calls are not affected.

      +

      +

      +
      +

      MAINTAINER

      +

      Jason Rhinelander

      +

      +

      +
      +

      SEE ALSO

      +

      the GT::Base manpage

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: AutoLoader.pm,v 1.13 2005/03/21 06:57:58 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/Base.html b/site/glist/templates/help/GT/Base.html new file mode 100644 index 0000000..be68e41 --- /dev/null +++ b/site/glist/templates/help/GT/Base.html @@ -0,0 +1,427 @@ + + + +GT::Base - Common base module to be inherited by all classes. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Base - Common base module to be inherited by all classes.

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::Base;
      +    use vars qw/@ISA $ATTRIBS $ERRORS/
      +    @ISA     = qw/GT::Base/;
      +    $ATTRIBS = {
      +        accessor  => default,
      +        accessor2 => default,
      +    };
      +    $ERRORS = {
      +        BADARGS => "Invalid argument: %s passed to subroutine: %s",
      +    };
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::Base is a base class that is used to provide common error handling, +debugging, creators and accessor methods.

      +

      To use GT::Base, simply make your module inherit from GT::Base. That +will provide the following functionality:

      +

      +

      +

      Debugging

      +

      Two new methods are available for debugging:

      +
      +    $self->debug($msg, [DEBUG_LEVEL]);
      +

      This will send a $msg to STDERR if the current debug level is greater +then the debug level passed in (defaults to 1).

      +
      +    $self->debug_level(DEBUG_LEVEL);
      +    Class->debug_level(DEBUG_LEVEL);
      +

      You can call debug_level() to set or get the debug level. It can +be set per object by calling it as an object method, or class wide +which will initilize all new objects with that debug level (only if +using the built in creator).

      +

      The debugging uses a package variable:

      +
      +    $Class::DEBUG = 0;
      +

      and assumes it exists.

      +

      +

      +

      Error Handling

      +

      Your object can now generate errors using the method:

      +
      +    $self->error(CODE, LEVEL, [args]);
      +

      CODE should be a key to a hash of error codes to user readable +error messages. This hash should be stored in $ERRORS which is +defined in your pacakge, or the package named in $ERROR_MESSAGE.

      +

      LEVEL should be either 'FATAL' or 'WARN'. If not specified it defaults +to FATAL. If it's a fatal error, the program will print the message +to STDERR and die.

      +

      args can be used to format the error message. For instance, you can +defined commonly used errors like:

      +
      +    CANTOPEN => "Unable to open file: '%s': %s"
      +

      in your $ERRORS hash. Then you can call error like:

      +
      +    open FILE, "somefile.txt"
      +        or return $self->error(CANTOPEN => FATAL => "somefile.txt", "$!");
      +

      The error handler will format your message using sprintf(), so all +regular printf formatting strings are allowed.

      +

      Since errors are kept within an array, too many errors can pose a +memory problem. To clear the error stack simply call:

      +
      +    $self->clear_errors();
      +

      +

      +

      Error Trapping

      +

      You can specify at run time to trap errors.

      +
      +    $self->catch_errors(\&code_ref);
      +

      which sets a $SIG{__DIE__} handler. Any fatal errors that occur, will +run your function. The function will not be run if the fatal was thrown +inside of an eval though.

      +

      +

      +

      Stack Trace

      +

      You can print out a stack trace at any time by using:

      +
      +    $self->stack_trace(1);
      +    Class->stack_trace(1);
      +

      If you pass in 1, the stack trace will be returned as a string, otherwise +it will be printed to STDOUT.

      +

      +

      +

      Accessor Methods

      +

      Using GT::Base automatically provides accessor methods for all your +attributes. By specifying:

      +
      +    $ATTRIBS = {
      +        attrib => 'default',
      +        ...
      +    };
      +

      in your package, you can now call:

      +
      +    my $val = $obj->attrib();
      +    $obj->attrib($set_val);
      +

      to set and retrieve the attributes for that value.

      +

      Note: This uses AUTOLOAD, so if you implement AUTOLOAD in your package, +you must have it fall back to GT::Base::AUTOLOAD if it fails. This +can be done with:

      +
      +    AUTOLOAD {
      +        ...
      +        goto &GT::Base::AUTOLOAD;
      +    }
      +

      which will pass all arguments as well.

      +

      +

      +

      Parameter Parsing

      +

      GT::Base also provides a method to parse parameters. In your methods you +can do:

      +
      +    my $self = shift;
      +    my $parm = $self->common_param(@_);
      +

      This will convert any of a hash reference, hash or CGI object into a hash +reference.

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Base.pm,v 1.132 2005/06/22 19:59:25 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/CGI.html b/site/glist/templates/help/GT/CGI.html new file mode 100644 index 0000000..c55c4b1 --- /dev/null +++ b/site/glist/templates/help/GT/CGI.html @@ -0,0 +1,474 @@ + + + +GT::CGI - a lightweight replacement for CGI.pm + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::CGI - a lightweight replacement for CGI.pm

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::CGI;
      +    my $in = new GT::CGI;
      +    foreach my $param ($in->param) {
      +        print "VALUE: $param => ", $in->param($param), "\n";
      +    }
      +
      +    use GT::CGI qw/-no_parse_buttons/;
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::CGI is a lightweight replacement for CGI.pm. It implements most of the +functionality of CGI.pm, with the main difference being that GT::CGI does not +provide a function-based interface (with the exception of the escape/unescape +functions, which can be called as either function or method), nor does it +provide the HTML functionality provided by CGI.pm.

      +

      The primary motivation for this is to provide a CGI module that can be shipped +with Gossamer products, not having to depend on a recent version of CGI.pm +being installed on remote servers. The secondary motivation is to provide a +module that loads and runs faster, thus speeding up Gossamer products.

      +

      Credit and thanks goes to the author of CGI.pm. A lot of the code (especially +file upload) was taken from CGI.pm.

      +

      +

      +

      param - Accessing form input.

      +

      Can be called as either a class method or object method. When called with no +arguments a list of keys is returned.

      +

      When called with a single argument in scalar context the first (and possibly +only) value is returned. When called in list context an array of values is +returned.

      +

      When called with two arguments, it sets the key-value pair.

      +

      +

      +

      header() - Printing HTTP headers

      +

      Can be called as a class method or object method. When called with no +arguments, simply returns the HTTP header.

      +

      Other options include:

      +
      +
      -force => 1
      +
      +
      +Force printing of header even if it has already been displayed. +
      +

      +
      -type => 'text/plain'
      +
      +
      +Set the type of the header to something other then text/html. +
      +

      +
      -cookie => $cookie
      +
      +
      +Display any cookies. You can pass in a single GT::CGI::Cookie object, or an +array of them. +
      +

      +
      -nph => 1
      +
      +
      +Display full headers for nph scripts. +
      +

      +

      If called with a single argument, sets the Content-Type.

      +

      +

      +

      redirect - Redirecting to new URL.

      +

      Returns a Location: header to redirect a user.

      +

      +

      +

      cookie - Set/Get HTTP Cookies.

      +

      Sets or gets a cookie. To retrieve a cookie:

      +
      +    my $cookie = $cgi->cookie ('key');
      +    my $cookie = $cgi->cookie (-name => 'key');
      +

      or to retrieve a hash of all cookies:

      +
      +    my $cookies = $cgi->cookie;
      +

      To set a cookie:

      +
      +    $c = $cgi->cookie (-name => 'foo', -value => 'bar')
      +

      You can also specify -expires for when the cookie should expire, -path for +which path the cookie valid, -domain for which domain the cookie is valid, and +-secure if the cookie is only valid for secure sites.

      +

      You would then set the cookie by passing it to the header function:

      +
      +    print $in->header ( -cookie => $c );
      +

      +

      +

      url - Retrieve the current URL.

      +

      Returns the current URL of the script. It defaults to display just the script +name and query string.

      +

      Options include:

      +
      +
      absolute => 1
      +
      +
      +Return the full URL: http://domain/path/to/script.cgi +
      +

      +
      relative => 1
      +
      +
      +Return only the script name: script.cgi +
      +

      +
      query_string => 1
      +
      +
      +Return the query string as well: script.cgi?a=b +
      +

      +
      path_info => 1
      +
      +
      +Returns the path info as well: script.cgi/foobar +
      +

      +
      remove_empty => 0
      +
      +
      +Removes empty query= from the query string. +
      +

      +

      +

      +

      get_hash - Return all form input as hash.

      +

      This returns the current parameters as a hash. Any values that have the same +key will be returned as an array reference of the multiple values.

      +

      +

      +

      escape - URL escape a string.

      +

      Returns the passed in value URL escaped. Can be called as class method or +object method.

      +

      +

      +

      unescape - URL unescape a string.

      +

      Returns the passed in value URL un-escaped. Can be called as class method or +object method. Optionally can take an array reference of strings instead of a +string. If called in this method, the values of the array reference will be +directly altered.

      +

      +

      +

      html_escape - HTML escape a string

      +

      Returns the passed in value HTML escaped. Translates &, <, > and `` to their +html equivalants.

      +

      +

      +

      html_unescape - HTML unescapes a string

      +

      Returns the passed in value HTML unescaped.

      +

      +

      +
      +

      DEPENDENCIES

      +

      Note: GT::CGI depends on the GT::Base manpage and the GT::AutoLoader manpage, and if you are +performing file uploads, GT::CGI::MultiPart, GT::CGI::Fh, and the GT::TempFile manpage. +The ability to set cookies requires GT::CGI::Cookie.

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: CGI.pm,v 1.145 2005/06/21 21:02:57 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/CGI/Action.html b/site/glist/templates/help/GT/CGI/Action.html new file mode 100644 index 0000000..43dc4d7 --- /dev/null +++ b/site/glist/templates/help/GT/CGI/Action.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/CGI/Action.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/CGI/Action/Common.html b/site/glist/templates/help/GT/CGI/Action/Common.html new file mode 100644 index 0000000..160dd46 --- /dev/null +++ b/site/glist/templates/help/GT/CGI/Action/Common.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/CGI/Action/Common.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/CGI/Action/Plugin.html b/site/glist/templates/help/GT/CGI/Action/Plugin.html new file mode 100644 index 0000000..f6944a4 --- /dev/null +++ b/site/glist/templates/help/GT/CGI/Action/Plugin.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/CGI/Action/Plugin.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/CGI/Cookie.html b/site/glist/templates/help/GT/CGI/Cookie.html new file mode 100644 index 0000000..a71d491 --- /dev/null +++ b/site/glist/templates/help/GT/CGI/Cookie.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/CGI/Cookie.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/CGI/EventLoop.html b/site/glist/templates/help/GT/CGI/EventLoop.html new file mode 100644 index 0000000..80a8a76 --- /dev/null +++ b/site/glist/templates/help/GT/CGI/EventLoop.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/CGI/EventLoop.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/CGI/Fh.html b/site/glist/templates/help/GT/CGI/Fh.html new file mode 100644 index 0000000..0fb3c1f --- /dev/null +++ b/site/glist/templates/help/GT/CGI/Fh.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/CGI/Fh.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/CGI/MultiPart.html b/site/glist/templates/help/GT/CGI/MultiPart.html new file mode 100644 index 0000000..10c6c55 --- /dev/null +++ b/site/glist/templates/help/GT/CGI/MultiPart.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/CGI/MultiPart.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/Cache.html b/site/glist/templates/help/GT/Cache.html new file mode 100644 index 0000000..4e869fa --- /dev/null +++ b/site/glist/templates/help/GT/Cache.html @@ -0,0 +1,343 @@ + + + +GT::Cache - Tied hash which caches output of functions. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Cache - Tied hash which caches output of functions.

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::Cache;
      +    my %cache;
      +    tie %cache, 'GT::Cache', $size, \&function;
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::Cache implements a simple but quick caching scheme for remembering +the results of functions. It also implements a max size to prevent +the cache from growing and drops least frequently requested entries +first, making it very useful under mod_perl.

      +

      +

      +
      +

      EXAMPLE

      +
      +    use GT::Cache;
      +    my %cache;
      +    tie %cache, 'GT::Cache', 100, \&complex_func;
      +    
      +    while (<>) {
      +        print "RESULT: ", $cache{$_}, "\n";
      +    }
      +
      +    sub complex_func {
      +        my $input = shift;
      +        # .. do complex work.
      +        return $output;
      +    }
      +

      This will cache the results of complex_func, and only run it when +the input is different. It stores a max of 100 entries at a time, +with the least frequently requested getting dropped first.

      +

      +

      +
      +

      NOTES

      +

      Currently, you can only pass as input to the function a single +scalar, and the output must be a single scalar. See the +Memoize module in CPAN for a much more robust implementation.

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Cache.pm,v 1.13 2004/01/13 01:35:15 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/Config.html b/site/glist/templates/help/GT/Config.html new file mode 100644 index 0000000..d890e13 --- /dev/null +++ b/site/glist/templates/help/GT/Config.html @@ -0,0 +1,601 @@ + + + +GT::Config - Dumped-hash configuration handler + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Config - Dumped-hash configuration handler

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::Config;
      +    my $Config = GT::Config->load($config_file);
      +    ...
      +    print $Config->{variable};
      +    ...
      +    $Config->{othervar} = "something";
      +    ...
      +    $Config->save;
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::Config provides a simple way to handle loading config files. It can load +and save any config file consisting of a dumped hash. You can then use the +object as if it were the actual hash reference from the config file. It +supports template set inheritance (see the GT::Template manpage) and mtime-based +caching.

      +

      +

      +
      +

      METHODS

      +

      +

      +

      load

      +

      There is no new() method. To get a new config object you do:

      +
      +    $Config = GT::Config->load("/path/to/config/file", { options });
      +

      The first argument is the full path to the file to open to read the +configuration. The file does not necessarily have to exist - see the options +below.

      +

      The second argument is a hash reference of options, and is optional. The +possible options are:

      +
      +
      inheritance
      +
      +
      +If provided as something true, GT::Config will scan for .tplinfo files looking +for inherited template sets. This is typically used for loading globals.txt or +language.txt files from Gossamer Threads products' template sets. +
      +
      +

      Defaults to off.

      +
      +

      +
      local
      +
      +
      +If provided as something true, GT::Config will look for a ``local'' directory +containing the file. When using inheritance, a ``local'' directory will also be +looked for in each inherited configuration file. However, regardless of the +inheritance option, ``local'' configuration files always inherit from their +non-local counterpart. +
      +
      +

      Additionally, this option causes GT::Config to save the file into a ``local'' +directory. Also note that the ``local'' file will _only_ contain keys that were +already in the local file, or were assigned to the config object after loading +the file.

      +
      +
      +

      Defaults to off.

      +
      +

      +
      cache
      +
      +
      +If provided, will look in the internal cache for a cached copy of the file. If +none is found, a new GT::Config object will be constructed as usual, then saved +in the cache. +
      +
      +

      Defaults to on. You must pass cache => 0 to disable cached loading. +Note that new objects are always stored in the cache, allowing you to specify +cache => 0 to force a reload of a cached file.

      +
      +

      +
      create_ok
      +
      +
      +If set, you'll still get back a GT::Config hash even if the file doesn't exist. +You can then save() the object to create a new config file. If this option is +not set, a fatal error will occur when attempting to load a file that does not +exist. +
      +
      +

      Defaults to off. Pass in create_ok => 1 if the config file doesn't +necessarily have to exist (i.e. when creating a new config file).

      +
      +

      +
      empty
      +
      +
      +The empty option is used to create a new, blank config file - it can be +thought of as a forced version of the create_ok option. It won't read +any files during loading (and as such completely ignores the inheritance +and cache options). This is mainly intended to be used when a complete +replacement of a file is desired, regardless of what is currently on disk. +
      +

      +
      chmod
      +
      +
      +The chmod option is used to specify the mode of the saved file. It must be +passed in octal form, such as 0644 (but not in string form, such as +"0644"). The default is 0666, to allow writing by any users. Though not +terribly secure, this is the sort of environment most CGI scripts require. +Setting a chmod value of undef instructs GT::Config to not perform a chmod. +
      +

      +
      strict
      +
      +
      +If set, a fatal error will occur when attempting to access a key of the config +file that does not exist. Note, however, that this only covers the first level +data structions - $CFG->{foo}->{bar} will not fatal if foo is a +hash ref, but bar is not set in that hash reference. $CFG->{foo} +(and $CFG->{foo}->{bar}) will fatal if the key foo does not exist +in the config data. +
      +

      +
      debug
      +
      +
      +If provided, debugging information will be printed. This will also cause a +warning to occur if fatal is disabled and load fails. +
      +
      +

      Defaults to disabled. Should not be used in production code, except when +debugging.

      +
      +

      +
      tmpfile
      +
      +
      +Instructs GT::Config to attempt to use a temporary file when saving. If used, +the contents will be written to a temporary file, then, if successfully +written, the temporary file will be moved to overwrite the real file. This +solves a couple of problems. Firstly, a full disk will never result in a +partial file as if the entire file is not written to the temporary file, it +will not overwrite the file already stored on disk. Secondly, it avoids a +potential problem with multiple processes attempting to write to the file at +the same time. +
      +
      +

      The following values are accepted:

      +
      +
      +
      +    0 - Do not use a temporary file
      +    undef - Use a temporary file if the base directory is writable
      +    1 - Always use a temporary file
      +
      +
      +

      The default is undef, which will attempt to use a temporary file is +possible, but won't fail if the script has permission to modify existing files, +but not to create new ones.

      +
      +

      +
      header
      +
      +
      +If provided, when saving a file this header will be written above the data. +Keep in mind that the file must be Perl-compilable, so be careful if you are +doing anything more than comments. +
      +
      +

      Note that the header may contain the string [localtime], which will be +replaced with the return value of scalar localtime() when saving, which is +generally a value such as: Sun Jan 25 15:12:26 2004.

      +
      +

      +
      tab
      +
      +
      +If provided, this will set what to use for tabs when calling save(). Defaults +to an actual tab, since that cuts down the file size over using multiple +spaces, while leaving the file readable. +
      +

      +
      compile_subs
      +
      +
      +If provided, any data starting with sub { will be compiled into a +subroutine. This compilation does not happen until the variable is accessed, +at which point a fatal error will occur if the code could not be compiled. The +code referenced will be cached (if using caching), but will be saved as the +original string (starting with sub {) when saving. +
      +
      +

      NOTE: The argument to compile_subs must be a valid perl package; the code +reference will be compiled in that package. For example, +compile_subs => 'GForum::Post' will compile the code ref in the +GForum::Post package. You need to do this to provide access to globals +variables such as $DB, $IN, etc.

      +
      +

      +
      sort_order
      +
      +
      +If provided, the option will be passed through as the 'order' option of +GT::Dumper for hash key ordering. See the GT::Dumper manpage. GT::Config always sorts +hash keys - this can be used when the default alphanumeric sort is not +sufficient. +
      +

      +

      +

      +

      save

      +

      To save a config file, simply call $object->save(). If the object uses +inheritance, only those keys that were not inherited (or were modified from the +inherited ones) will be saved.

      +
      +    $Config->save();
      +

      NOTE: ALWAYS SAVE AFTER MAKING ANY CHANGES!!!. If you do not save after +making changes, the data retrieved from the cache may not be the same as the +data stored in the configuration file on disk. After making ANY changes make +absolutely sure that you either undo the change or save the configuration file.

      +

      +

      +

      cache_hit

      +

      Returns whether or not the current object was loaded from cache (1) or loaded +from disk (undef).

      +

      +

      +

      inheritance

      +

      Returns the inheritance status (1 or 0) of the object.

      +

      +

      +

      create_ok

      +

      Returns the status (1 or 0) of the ``create_ok'' flag.

      +

      +

      +

      tmpfile

      +

      With no arguments, returns whether or not the object will attempt to use a +temporary file when saving. Possible values are:

      +
      +    0 - Do not use a temporary file
      +    undef - Use a temporary file if the base directory is writable
      +    1 - Always use a temporary file
      +

      You can pass in a single argument of one of the above values to set whether or +not the object will use a temporary file when saving.

      +

      +

      +

      cache

      +

      This method returns whether or not the object is cached. This cannot be +enabled/disabled after loading a config file; you must specify it as an +argument to load() instead.

      +

      +

      +

      debug_level

      +

      This method returns the current debug level.

      +

      You may provide one argument which sets a new debug level.

      +

      0 means no debugging, 1 means basic debugging, 2 means heavy debugging.

      +

      If setting a new debug level, the old debug level is returned.

      +

      +

      +

      header

      +

      This method returns or sets the header that will be printed when saving.

      +

      With no arguments, returns the header.

      +

      You may provide one argument which sets a new header. Keep in mind that the +file must be Perl-compilable, so take care if doing anything other than +comments.

      +

      If providing a new header, the old header is returned.

      +

      Note that the header may contain the value [localtime], which will be +replaced with the return value of scalar localtime() when saving.

      +

      +

      +

      sort_order

      +

      This method returns or sets a code reference to be passed through as the +'order' option of GT::Dumper for hash key ordering. See the GT::Dumper manpage. +GT::Config always sorts hash keys - this can be used when the default +alphanumeric sort is not sufficient.

      +

      +

      +
      +

      SEE ALSO

      +

      the GT::Template::Inheritance manpage

      +

      +

      +
      +

      MAINTAINER

      +

      Jason Rhinelander

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      $Id: Config.pm,v 1.45 2005/03/21 05:49:39 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/Date.html b/site/glist/templates/help/GT/Date.html new file mode 100644 index 0000000..efe9f38 --- /dev/null +++ b/site/glist/templates/help/GT/Date.html @@ -0,0 +1,489 @@ + + + +GT::Date - Common date parsing and manipulation routines + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Date - Common date parsing and manipulation routines

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::Date qw/:all/;
      +    my $date = date_get();
      +    my $next_week = date_add($date, 7);
      +    my $is_bigger = date_is_greater($date, $next_week);
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::Date provides several functions useful in parsing dates, and +doing date manipulation. Under the hood, it uses Time::Local +code to transform a date into seconds for comparison and +mathematical operations. It also uses the GT::Cache manpage to store +most of the complex work.

      +

      No functions are exported by default. You can either specify +the functions you need in use, or use the tags ':all' or +':timelocal'. All will give you all functions, and timelocal +will give you functions found in Time::Local.

      +

      GT::Date uses a package global $DATE_FMT which specifies +the format that dates should be returned in. You can change this using +the date_set_format() function.

      +

      +

      +

      date_is_valid

      +

      Returns 1 if the argument passed in is a valid date. It must first +be in the current date format, and then be a valid date.

      +

      +

      +

      date_is_greater

      +

      Returns 1 if argument 1 is greater then argument 2, otherwise 0.

      +

      +

      +

      date_is_smaller

      +

      Returns 1 if argument 1 is smaller then argument 2, otherwise 0.

      +

      +

      +

      date_get date_get_gm

      +

      Called with no arguments, returns the current date based on system +time. You can specify the date you want by passing in the seconds +since epoch (output of time()).

      +

      +

      +

      date_comp

      +

      Equivalent to arg1 <=> arg2.

      +

      +

      +

      date_diff

      +

      Returns number of days difference between arg1 - arg2.

      +

      +

      +

      date_add date_add_gm

      +

      Returns date derived from arg1 + arg2, where the second argument +can be either a date or number of days.

      +

      +

      +

      date_sub date_sub_gm

      +

      Returns date derived from arg1 - arg2, where the second argument +can be either a date or number of days.

      +

      +

      +

      timegm

      +

      Takes the returned array from gmtime() and returns a unix time +stamp.

      +

      +

      +

      timlocal

      +

      Takes the array returned by localtime() and returns a unix time +stamp.

      +

      +

      +

      parse_format

      +

      Takes a string and a date format and returns an array +ref of the first 7 arguments returned by localtime().

      +

      +

      +

      format_date

      +

      Takes a localtime array, and a format string and returns a string +of the parsed format.

      +

      +

      +

      Setting date format

      +

      You can use date_set_format to change the format. You pass in a +format string. It is made up of:

      +
      +    %yyyy%      four digit year as in 1999
      +    %yy%        two digit year as in 99
      +    %y%         two digit year without leading 0
      +    %mmmm%      long month name as in January
      +    %mmm%       short month name as in Jan
      +    %mm%        numerical month name as in 01
      +    %m%         numerical month name without leading 0 as in 1
      +    %dddd%      long day name as in Sunday
      +    %ddd%       short day name as in Sun
      +    %dd%        numerical date
      +    %d%         numerical date without leading 0
      +    %HH%        two digit hour, 24 hour time
      +    %H%         one or two digit hour, 24 hour time
      +    %hh%        two digit hour, 12 hour time. 0 becomes 12.
      +    %h%         one or two digit hour, 12 hour time. 0 becomes 12.
      +    %MM%        two digit minute
      +    %M%         one or two digit minute (when would someone ever WANT this?)
      +    %ss%        two digit second
      +    %s%         one ot two digit second (when would someone ever WANT this?)
      +    %tt%        AM or PM (use with 12 hour time)
      +    %o%         + or - GMT offset
      +

      Common formats include:

      +
      +    %yyyy%-%mm%-%dd%            1999-12-25
      +    %dd%-%mmm%-%yyyy%           12-Dec-1999
      +    %ddd% %mmm% %dd% %yyyy%     Sat Dec 12 1999
      +    %ddd% %mmm% %dd% %yyyy%     Sat Dec 12 1999
      +

      or RFC822 mime mail format:

      +
      +     %ddd%, %dd% %mmm% %yyyy% %HH%:%MM%:%ss% %o%   Sat, 12, Dec 1999 21:32:02 -0800
      +

      or MySQL format:

      +
      +    %yyyy%-%mm%-%dd% %HH%:%MM%:%ss%  1999-03-25 21:32:02
      +

      The language used for month names and day names can be changed with +date_set_month(), date_set_days(), date_set_days_short() and +date_set_month_short().

      +

      +

      +

      Transforming between date formats.

      +

      You can transform a date from one format to another with:

      +
      +    date_transform ($date, $orig_fmt, $new_fmt);
      +

      where $orig_fmt and $new_fmt are date format strings described above.

      +

      +

      +

      Getting the GM offset.

      +

      You can get the number of seconds between the system time and GM time +using:

      +
      +    my $time = date_gmt_offset();
      +

      So if you are in Pacific time, it would return 25200 seconds (-0700 time zone).

      +

      +

      +
      +

      EXAMPLES

      +

      Get todays date, the default format unless specified is yyyy-mm-dd.

      +
      +    print date_get();                 2000-12-31
      +

      Get todays date in a different format:

      +
      +    date_set_format('%ddd% %mmm% %dd% %yyyy%');
      +    print date_get();                               Sat Dec 31 2000
      +

      Get the date from 1 week ago.

      +
      +    # Long way
      +    my $date1 = date_get();
      +    my $date2 = date_sub($date1, 7);
      +
      +        or
      +
      +    # Can pass in unix timestamp of date we want.
      +    my $date = date_get (time - (7 * 86400));
      +

      Compare two dates.

      +
      +    my $halloween = '2000-10-31';
      +    my $christmas = '2000-12-25';
      +    if (date_is_smaller($halloween, $christmas)) {
      +        print "Halloween comes before christmas!";
      +    }
      +    if (date_is_greater($christmas, $halloween)) {
      +        print "Yup, christmas comes after halloween.";
      +    }
      +    my @dates = ($halloween, $christmas);
      +    print "Dates in order: ", sort date_comp @dates;
      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Date.pm,v 1.75 2005/04/04 22:21:23 brewt Exp $

      + + + + diff --git a/site/glist/templates/help/GT/Delay.html b/site/glist/templates/help/GT/Delay.html new file mode 100644 index 0000000..7329d1a --- /dev/null +++ b/site/glist/templates/help/GT/Delay.html @@ -0,0 +1,385 @@ + + + +GT::Delay - Generic delayed module loading + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Delay - Generic delayed module loading

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::Delay;
      +
      +    my $obj = GT::Delay('GT::Foo', 'HASH', foo => "bar", bar => 12);
      +
      +    ... # time passes without using $obj
      +
      +    $obj->method();
      +

      +

      +
      +

      DESCRIPTION

      +

      This module provides a simple way to handle delayed module loading in a fairly +generic way. Your object will only be a very lightweight GT::Delay object +until you call a method on it, at which point the desired module will be loaded, +your object will be changed into an object of the desired type.

      +

      +

      +
      +

      FUNCTIONS

      +

      There is only one usable function provided by this module, GT::Delay() (not +GT::Delay::Delay as this module attempts to leave the GT::Delay namespace as +empty as possible).

      +

      +

      +

      GT::Delay

      +

      GT::Delay is used to create a new delayed object. It takes at least two +arguments. The first is the package to load, such as 'GT::Foo' to require +GT/Foo.pm and create a new GT::Foo object. The second is the type of blessed +data structure a 'GT::Foo' object really is. This can be one of either 'HASH', +'ARRAY', or 'SCALAR'. Any additional arguments are kept and passed in as +arguments to the new() method of the object when created.

      +

      The object type ('HASH', 'ARRAY', or 'SCALAR') is needed is to get around a +caveat of references - if $a and $b both point to the same reference, $b cannot +be changed from $a - which makes it impossible to just get a new object and +replace $_[0] with that object, because although that would change one of +either $a or $b, it wouldn't change the other and you could easily end up with +two separate objects. When a method is called, the new object is created, then +copied into the original object which is then reblessed into the desired +package. This doesn't change either $a or $b, but rather changes the reference +they point to. You have to pass the object type because the reference must be +reblessed, but the underlying data type cannot change. Unfortunately, this +approach has a few caveats of its own, listed below.

      +

      +

      +
      +

      CAVEATS and LIMITATIONS

      +

      Modules that are created by a method other than new() are not supported.

      +

      Modules that use a namespace different from the module location are not +supported. For example, a package Foo::Bar::Blah located in Foo/Bar.pm. If +you have such a module that would benefit from delayed loading, you need to +rethink your package/filename naming scheme, or not use this module. It _is_ +possible to do this with a hack such as: +$INC{'Foo/Bar/Blah.pm'} = './Foo/Bar.pm'; - but other than for testing, +doing such a thing is strongly discouraged.

      +

      Objects cannot have their elements directly accessed - for example, +$obj->{foo}. But, since that is bad practise anyway, it isn't that much +of a limitation. That said, objects _can_ be accessed directly _after_ any +method has been called.

      +

      Modules that store a string or integer form of $self (GT::Config does this to +store object attributes) will not work, since the working object will not be +the same object create a new(), but rather a copy.

      +

      Modules with DESTROY methods that do things to references in $self (for +example, delete $self->{foo}->{bar} - though delete +$self-E<gt>{foo} would be safe) will most likely not work properly as the copy +is not deep - i.e. references are copied as-is.

      +

      Along the same lines as the previous point, the first object will be destroyed +before the first method call goes through, so modules that do things (e.g. +delete files, close filehandles, etc.) in DESTROY will most likely not work.

      +

      Any module that doesn't fall into any of the points above will be perfectly +well supported by this module.

      +

      +

      +
      +

      MAINTAINER

      +

      Jason Rhinelander

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Delay.pm,v 1.4 2004/01/13 01:35:15 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/Dumper.html b/site/glist/templates/help/GT/Dumper.html new file mode 100644 index 0000000..13c673f --- /dev/null +++ b/site/glist/templates/help/GT/Dumper.html @@ -0,0 +1,465 @@ + + + +GT::Dumper - Convert Perl data structures into a string. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Dumper - Convert Perl data structures into a string.

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::Dumper;
      +    print Dumper($complex_var);
      +    print GT::Dumper->dump ( var => '$MYVAR', data => $complex_var);
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::Dumper by default exports a method Dumper() which will +behave similar to Data::Dumper's Dumper(). It differs in that +it will only take a single argument, and the variable dumped +will be $VAR instead of $VAR1. Also, to provide easier control +to change the variable name that gets dumped, you can use:

      +
      +    GT::Dumper->dump ( var => string, data => yourdata );
      +

      and the dump will start with string = instead of $VAR = .

      +

      +

      +
      +

      EXAMPLE

      +
      +    use GT::Dumper;
      +    my %foo;
      +    my @bar = (1, 2, 3);
      +    $foo{alpha} = \@bar;
      +    $foo{beta} = 'a string';
      +    print Dumper(\%foo);
      +

      This will print:

      +
      +    $VAR = {
      +        'beta' => 'a string',
      +        'alpha' => [
      +            '1',
      +            '2',
      +            '3',
      +        ],
      +    };
      +

      +

      +
      +

      METHODS/FUNCTIONS

      +

      +

      +

      Dumper

      +

      Dumper() is exported by default when using GT::Dumper. It takes a single +variable and returns a string representation of the variable. The string can +then be eval()'ed back into the same data structure.

      +

      It takes only one argument - the variable to dump. The return is a string of +the form:

      +

      $VAR = DATA

      +

      where 'DATA' is the actual data structure of the variable. A more powerful and +customizable dumping method is the dump method.

      +

      +

      +

      dump

      +

      dump() provides a more customizable method to dumping a data structure. Through +the various options available, listed below, the output of a data structure +dump can be formatted in several different ways.

      +

      The options are as follows. Only the data option is required.

      +
        +
      • data
        +
      • +The data option takes a data structure to dump. It is required. +

        +
      • var
        +
      • +By default, a dump is output as an assignment to $VAR. For example, dumping +the string foo would return: $VAR = 'foo'. You can change and even omit +the assignment using the var option. To specify a different variable, you +simply specify it as the value here. To have 'foo' dump as just 'foo' +instead of $VAR = 'foo', specify var as an empty string, or undef. +

        +
      • tab
        +
      • +When indenting for complex data structures (array refs, hash refs, etc.) an +indent is used. By default, the indent is 4 spaces, however you can change this +by using the tab option. +

        +
      • sort
        +
      • +The sort option enables hash key sorting. It is not on by default - to +enable, simply specify the sort option with 1 as the value. The default sort +method is case-sensitive alphabetical. See the order option for +specifying your own sort order. +

        +
      • order
        +
      • +When sorting, it is sometimes desirable to use a custom sort order rather than +the default case-sensitive alphabetical sort. The order option takes a code +reference and enables custom sort ordering. The code reference will be passed 4 +variables. The first and second are the two items being compared - $a and $b in +Perl's sort mechanism. The third and fourth are the values in the hash being +sorted. The code reference, like a Perl sort routine, should return -1 if $a +should come before $b, 0 if $a and $b are equivelant in your sort order, and 1 +if $b should come before $a. Because of scoping and package issues in Perl, it +is not possible to directly use $a and $b. +

        +
      • compress
        +
      • +The default dump method is to use ' => ' between hash key and value, to use +indenting, and to add a line break after each dumped element. You can turn all +of these off by using the compress option. +

        Compression removes all non-essential characters from the output, thus reducing +data size, however also generally making the dump very difficult to read. If +enabled, the dumping behaviour is changed as follows:

        +
          +
        • assignment
          +
        • +If using a var (ie. $VAR = DATA), the spaces around the = will be stripped. +The output will look like: $VAR=DATA +

          +
        • hash keys
          +
        • +Instead of placing the 4 characters ' => ' between hash keys and values, a +single ',' will be used. +

          +
        • tabs
          +
        • +Tabs will not be used. +

          +
        • newlines
          +
        • +Normally, a newline character is added after each dumped element. Compress +turns this off. +

        +
      • structure
        +
      • +The structure option causes the dump to be a valid perl structure rather than a +valid perl statement. This differs in two ways - for one, the var option is +ignored - it is treated as if a blank var was entered, thereby not returning +an assignment. The other difference is that an an ordinary dump adds a +semicolon and newline at the end of the dump, but these are not added when the +structure option is enabled. +

      +

      +

      +

      dump_structure

      +

      This is a quick method to do a structure dump. It takes one argument - the data +to dump. Calling: + $class->dump_structure($DATA); +is identical to calling: + $class->dump(data => $DATA, structure => 1); +See the structure option.

      +

      +

      +
      +

      SEE ALSO

      +

      the Data::Dumper manpage

      +

      +

      +
      +

      MAINTAINER

      +

      Jason Rhinelander

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Dumper.pm,v 1.38 2005/02/18 04:44:33 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/File/Diff.html b/site/glist/templates/help/GT/File/Diff.html new file mode 100644 index 0000000..8124d34 --- /dev/null +++ b/site/glist/templates/help/GT/File/Diff.html @@ -0,0 +1,667 @@ + + + +Algorithm::Diff - Compute `intelligent' differences between two files / lists + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      Algorithm::Diff - Compute `intelligent' differences between two files / lists

      +

      +

      +
      +

      SYNOPSIS

      +
      +  use GT::File::Diff qw(diff sdiff LCS traverse_sequences
      +                         traverse_balanced);
      +
      +  @lcs    = LCS( \@seq1, \@seq2 );
      +
      +  @lcs    = LCS( \@seq1, \@seq2, $key_generation_function );
      +
      +  $lcsref = LCS( \@seq1, \@seq2 );
      +
      +  $lcsref = LCS( \@seq1, \@seq2, $key_generation_function );
      +
      +  @diffs = diff( \@seq1, \@seq2 );
      +
      +  @diffs = diff( \@seq1, \@seq2, $key_generation_function );
      +
      +  @sdiffs = sdiff( \@seq1, \@seq2 );
      +
      +  @sdiffs = sdiff( \@seq1, \@seq2, $key_generation_function );
      +  
      +  traverse_sequences( \@seq1, \@seq2,
      +                     { MATCH => $callback,
      +                       DISCARD_A => $callback,
      +                       DISCARD_B => $callback,
      +                     } );
      +
      +  traverse_sequences( \@seq1, \@seq2,
      +                     { MATCH => $callback,
      +                       DISCARD_A => $callback,
      +                       DISCARD_B => $callback,
      +                     },
      +                     $key_generation_function );
      +
      +  traverse_balanced( \@seq1, \@seq2,
      +                     { MATCH => $callback,
      +                       DISCARD_A => $callback,
      +                       DISCARD_B => $callback,
      +                       CHANGE    => $callback,
      +                     } );
      +

      +

      +
      +

      INTRODUCTION

      +

      (by Mark-Jason Dominus)

      +

      I once read an article written by the authors of diff; they said +that they hard worked very hard on the algorithm until they found the +right one.

      +

      I think what they ended up using (and I hope someone will correct me, +because I am not very confident about this) was the `longest common +subsequence' method. in the LCS problem, you have two sequences of +items:

      +
      +        a b c d f g h j q z
      +
      +        a b c d e f g i j k r x y z
      +

      and you want to find the longest sequence of items that is present in +both original sequences in the same order. That is, you want to find +a new sequence S which can be obtained from the first sequence by +deleting some items, and from the secend sequence by deleting other +items. You also want S to be as long as possible. In this case +S is

      +
      +        a b c d f g j z
      +

      From there it's only a small step to get diff-like output:

      +
      +        e   h i   k   q r x y 
      +        +   - +   +   - + + +
      +

      This module solves the LCS problem. It also includes a canned +function to generate diff-like output.

      +

      It might seem from the example above that the LCS of two sequences is +always pretty obvious, but that's not always the case, especially when +the two sequences have many repeated elements. For example, consider

      +
      +        a x b y c z p d q
      +        a b c a x b y c z
      +

      A naive approach might start by matching up the a and b that +appear at the beginning of each sequence, like this:

      +
      +        a x b y c         z p d q
      +        a   b   c a b y c z
      +

      This finds the common subsequence a b c z. But actually, the LCS +is a x b y c z:

      +
      +              a x b y c z p d q
      +        a b c a x b y c z
      +

      +

      +
      +

      USAGE

      +

      This module provides three exportable functions, which we'll deal with in +ascending order of difficulty: LCS, +diff, sdiff, traverse_sequences, and traverse_balanced.

      +

      +

      +

      LCS

      +

      Given references to two lists of items, LCS returns an array containing their +longest common subsequence. In scalar context, it returns a reference to +such a list.

      +
      +  @lcs    = LCS( \@seq1, \@seq2 );
      +  $lcsref = LCS( \@seq1, \@seq2 );
      +

      LCS may be passed an optional third parameter; this is a CODE +reference to a key generation function. See KEY GENERATION FUNCTIONS.

      +
      +  @lcs    = LCS( \@seq1, \@seq2, $keyGen );
      +  $lcsref = LCS( \@seq1, \@seq2, $keyGen );
      +

      Additional parameters, if any, will be passed to the key generation +routine.

      +

      +

      +

      diff

      +
      +  @diffs     = diff( \@seq1, \@seq2 );
      +  $diffs_ref = diff( \@seq1, \@seq2 );
      +

      diff computes the smallest set of additions and deletions necessary +to turn the first sequence into the second, and returns a description +of these changes. The description is a list of hunks; each hunk +represents a contiguous section of items which should be added, +deleted, or replaced. The return value of diff is a list of +hunks, or, in scalar context, a reference to such a list.

      +

      Here is an example: The diff of the following two sequences:

      +
      +  a b c e h j l m n p
      +  b c d e f j k l m r s t
      +

      Result:

      +
      + [ 
      +   [ [ '-', 0, 'a' ] ],
      +
      +   [ [ '+', 2, 'd' ] ],
      +
      +   [ [ '-', 4, 'h' ] , 
      +     [ '+', 4, 'f' ] ],
      +
      +   [ [ '+', 6, 'k' ] ],
      +
      +   [ [ '-', 8, 'n' ], 
      +     [ '-', 9, 'p' ], 
      +     [ '+', 9, 'r' ], 
      +     [ '+', 10, 's' ], 
      +     [ '+', 11, 't' ],
      +   ]
      + ]
      +

      There are five hunks here. The first hunk says that the a at +position 0 of the first sequence should be deleted (-). The second +hunk says that the d at position 2 of the second sequence should +be inserted (+). The third hunk says that the h at position 4 +of the first sequence should be removed and replaced with the f +from position 4 of the second sequence. The other two hunks similarly.

      +

      diff may be passed an optional third parameter; this is a CODE +reference to a key generation function. See KEY GENERATION FUNCTIONS.

      +

      Additional parameters, if any, will be passed to the key generation +routine.

      +

      +

      +

      sdiff

      +
      +  @sdiffs     = sdiff( \@seq1, \@seq2 );
      +  $sdiffs_ref = sdiff( \@seq1, \@seq2 );
      +

      sdiff computes all necessary components to show two sequences +and their minimized differences side by side, just like the +Unix-utility sdiff does:

      +
      +    same             same
      +    before     |     after
      +    old        <     -
      +    -          >     new
      +

      It returns a list of array refs, each pointing to an array of +display instructions. In scalar context it returns a reference +to such a list.

      +

      Display instructions consist of three elements: A modifier indicator +(+: Element added, -: Element removed, u: Element unmodified, +c: Element changed) and the value of the old and new elements, to +be displayed side by side.

      +

      An sdiff of the following two sequences:

      +
      +  a b c e h j l m n p
      +  b c d e f j k l m r s t
      +

      results in

      +

      [ [ '-', 'a', '' ], + [ 'u', 'b', 'b' ], + [ 'u', 'c', 'c' ], + [ '+', '', 'd' ], + [ 'u', 'e', 'e' ], + [ 'c', 'h', 'f' ], + [ 'u', 'j', 'j' ], + [ '+', '', 'k' ], + [ 'u', 'l', 'l' ], + [ 'u', 'm', 'm' ], + [ 'c', 'n', 'r' ], + [ 'c', 'p', 's' ], + [ '+', '', 't' ] ]

      +

      sdiff may be passed an optional third parameter; this is a CODE +reference to a key generation function. See KEY GENERATION FUNCTIONS.

      +

      Additional parameters, if any, will be passed to the key generation +routine.

      +

      +

      +

      traverse_sequences

      +

      traverse_sequences is the most general facility provided by this +module; diff and LCS are implemented as calls to it.

      +

      Imagine that there are two arrows. Arrow A points to an element of sequence A, +and arrow B points to an element of the sequence B. Initially, the arrows +point to the first elements of the respective sequences. traverse_sequences +will advance the arrows through the sequences one element at a time, calling an +appropriate user-specified callback function before each advance. It +willadvance the arrows in such a way that if there are equal elements $A[$i] +and $B[$j] which are equal and which are part of the LCS, there will be +some moment during the execution of traverse_sequences when arrow A is +pointing to $A[$i] and arrow B is pointing to $B[$j]. When this happens, +traverse_sequences will call the MATCH callback function and then it will +advance both arrows.

      +

      Otherwise, one of the arrows is pointing to an element of its sequence that is +not part of the LCS. traverse_sequences will advance that arrow and will +call the DISCARD_A or the DISCARD_B callback, depending on which arrow it +advanced. If both arrows point to elements that are not part of the LCS, then +traverse_sequences will advance one of them and call the appropriate +callback, but it is not specified which it will call.

      +

      The arguments to traverse_sequences are the two sequences to traverse, and a +hash which specifies the callback functions, like this:

      +
      +  traverse_sequences( \@seq1, \@seq2,
      +                     { MATCH => $callback_1,
      +                       DISCARD_A => $callback_2,
      +                       DISCARD_B => $callback_3,
      +                     } );
      +

      Callbacks for MATCH, DISCARD_A, and DISCARD_B are invoked with at least the +indices of the two arrows as their arguments. They are not expected to return +any values. If a callback is omitted from the table, it is not called.

      +

      Callbacks for A_FINISHED and B_FINISHED are invoked with at least the +corresponding index in A or B.

      +

      If arrow A reaches the end of its sequence, before arrow B does, +traverse_sequences will call the A_FINISHED callback when it advances +arrow B, if there is such a function; if not it will call DISCARD_B instead. +Similarly if arrow B finishes first. traverse_sequences returns when both +arrows are at the ends of their respective sequences. It returns true on +success and false on failure. At present there is no way to fail.

      +

      traverse_sequences may be passed an optional fourth parameter; this is a +CODE reference to a key generation function. See KEY GENERATION FUNCTIONS.

      +

      Additional parameters, if any, will be passed to the key generation function.

      +

      +

      +

      traverse_balanced

      +

      traverse_balanced is an alternative to traverse_sequences. It +uses a different algorithm to iterate through the entries in the +computed LCS. Instead of sticking to one side and showing element changes +as insertions and deletions only, it will jump back and forth between +the two sequences and report changes occurring as deletions on one +side followed immediatly by an insertion on the other side.

      +

      In addition to the +DISCARD_A, +DISCARD_B, and +MATCH +callbacks supported by traverse_sequences, traverse_balanced supports +a CHANGE callback indicating that one element got replaced by another:

      +
      +  traverse_sequences( \@seq1, \@seq2,
      +                     { MATCH => $callback_1,
      +                       DISCARD_A => $callback_2,
      +                       DISCARD_B => $callback_3,
      +                       CHANGE    => $callback_4,
      +                     } );
      +

      If no CHANGE callback is specified, traverse_balanced +will map CHANGE events to DISCARD_A and DISCARD_B actions, +therefore resulting in a similar behaviour as traverse_sequences +with different order of events.

      +

      traverse_balanced might be a bit slower than traverse_sequences, +noticable only while processing huge amounts of data.

      +

      The sdiff function of this module +is implemented as call to traverse_balanced.

      +

      +

      +
      +

      KEY GENERATION FUNCTIONS

      +

      diff, LCS, and traverse_sequences accept an optional last parameter. +This is a CODE reference to a key generating (hashing) function that should +return a string that uniquely identifies a given element. It should be the +case that if two elements are to be considered equal, their keys should be the +same (and the other way around). If no key generation function is provided, +the key will be the element as a string.

      +

      By default, comparisons will use ``eq'' and elements will be turned into keys +using the default stringizing operator '``'''.

      +

      Where this is important is when you're comparing something other than strings. +If it is the case that you have multiple different objects that should be +considered to be equal, you should supply a key generation function. Otherwise, +you have to make sure that your arrays contain unique references.

      +

      For instance, consider this example:

      +
      +  package Person;
      +
      +  sub new
      +  {
      +    my $package = shift;
      +    return bless { name => '', ssn => '', @_ }, $package;
      +  }
      +
      +  sub clone
      +  {
      +    my $old = shift;
      +    my $new = bless { %$old }, ref($old);
      +  }
      +
      +  sub hash
      +  {
      +    return shift()->{'ssn'};
      +  }
      +
      +  my $person1 = Person->new( name => 'Joe', ssn => '123-45-6789' );
      +  my $person2 = Person->new( name => 'Mary', ssn => '123-47-0000' );
      +  my $person3 = Person->new( name => 'Pete', ssn => '999-45-2222' );
      +  my $person4 = Person->new( name => 'Peggy', ssn => '123-45-9999' );
      +  my $person5 = Person->new( name => 'Frank', ssn => '000-45-9999' );
      +

      If you did this:

      +
      +  my $array1 = [ $person1, $person2, $person4 ];
      +  my $array2 = [ $person1, $person3, $person4, $person5 ];
      +  GT::File::Diff::diff( $array1, $array2 );
      +

      everything would work out OK (each of the objects would be converted +into a string like ``Person=HASH(0x82425b0)'' for comparison).

      +

      But if you did this:

      +
      +  my $array1 = [ $person1, $person2, $person4 ];
      +  my $array2 = [ $person1, $person3, $person4->clone(), $person5 ];
      +  GT::File::Diff::diff( $array1, $array2 );
      +

      $person4 and $person4->clone() (which have the same name and SSN) +would be seen as different objects. If you wanted them to be considered +equivalent, you would have to pass in a key generation function:

      +
      +  my $array1 = [ $person1, $person2, $person4 ];
      +  my $array2 = [ $person1, $person3, $person4->clone(), $person5 ];
      +  GT::File::Diff::diff( $array1, $array2, \&Person::hash );
      +

      This would use the 'ssn' field in each Person as a comparison key, and +so would consider $person4 and $person4->clone() as equal.

      +

      You may also pass additional parameters to the key generation function +if you wish.

      +

      +

      +
      +

      AUTHOR

      +

      This version by Ned Konz, perl@bike-nomad.com

      +

      +

      +
      +

      LICENSE

      +

      Copyright (c) 2000-2002 Ned Konz. All rights reserved. +This program is free software; +you can redistribute it and/or modify it under the same terms +as Perl itself.

      +

      +

      +
      +

      CREDITS

      +

      Versions through 0.59 (and much of this documentation) were written by:

      +

      Mark-Jason Dominus, mjd-perl-diff@plover.com

      +

      This version borrows the documentation and names of the routines +from Mark-Jason's, but has all new code in Diff.pm.

      +

      This code was adapted from the Smalltalk code of +Mario Wolczko <mario@wolczko.com>, which is available at +ftp://st.cs.uiuc.edu/pub/Smalltalk/MANCHESTER/manchester/4.0/diff.st

      +

      sdiff and traverse_balanced were written by Mike Schilli +<m@perlmeister.com>.

      +

      The algorithm is that described in +A Fast Algorithm for Computing Longest Common Subsequences, +CACM, vol.20, no.5, pp.350-353, May 1977, with a few +minor improvements to improve the speed.

      + + + + diff --git a/site/glist/templates/help/GT/File/Tools.html b/site/glist/templates/help/GT/File/Tools.html new file mode 100644 index 0000000..31eba3f --- /dev/null +++ b/site/glist/templates/help/GT/File/Tools.html @@ -0,0 +1,580 @@ + + + +GT::File::Tools - Export tools for dealing with files + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::File::Tools - Export tools for dealing with files

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::File::Tools qw/:all/;
      +    
      +    # Find all files in a users home directory.
      +    find "/home/user", sub { print shift };
      +    
      +    # Rename a file1 to file2.
      +    move "file1", "file2";
      +
      +    # Remove a list of files.
      +    del @files;
      +
      +    # Remove a users home directory
      +    deldir "/home/foo";
      +
      +    # Copy a file
      +    copy "file1", "file2";
      +
      +    # Recursively copy a directory.
      +    copy "/home/user", "/home/user.bak";
      +
      +    # Recursively make a directory.
      +    mkpath "/home/user/www/cgi-bin", 0755;
      +
      +    # Parse a filename into directory, file and is_relative components
      +    my ($dir, $file, $is_rel) = parsefile("/home/foo/file.txt");
      +
      +    # Get the file portion of a filename
      +    my $file = basename("/home/foo/file.txt");
      +
      +    # Get the directory portion of a filename.
      +    my $dir = dirname("/home/foo/file.txt");
      +
      +    # Use shell like expansion to get a list of absolute files.
      +    my @src = expand("*.c", "*.h");
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::File::Tools is designed to export requested functions into your namespace. +These function perform various file operations.

      +

      +

      +
      +

      FUNCTIONS

      +

      GT::File::Tools exports functions to your namespace. Here is a list of the +functions you can request to be exported.

      +

      +

      +

      find

      +

      find takes three parameters: directory to search in, callback to run for +each file and/or directory found, and a hash ref of options. Note: this is +the opposite order of File::Find's find() function! The following options +can be passed set:

      +
      +
      globbing
      +
      +
      +Expand filenames in the same way as the unix shell: +
      +
      +
      +    find("/home/a*", sub { print shift; }, { globbing => 1 });
      +
      +
      +

      would fine all home directories starting with the letter a. This option is +off by default.

      +
      +

      +
      error_handler
      +
      +
      +A code ref that is run whenever find encounters an error. If the callback +returns 0, find will stop immediately, otherwise find will continue +searching (default). +
      +

      +
      no_chdir
      +
      +
      +By default, find will chdir into the directories it is searching as +this results in a dramatic performance improvement. Upon completion, find +will chdir back to the original directory. This behavior is on by default. +
      +

      +
      dirs_first
      +
      +
      +This option controls the order find traverses. It defaults on, and means +find will go down directories first before looking at files. This is +essential for recursively deleting a directory. +
      +

      +
      files_only
      +
      +
      +This option tells find to run the callback only for each file found +and not for each directory. Off by default. +
      +

      +
      dirs_only
      +
      +
      +This option tells find to run the callback only for each directory found +and not for each file. Off by default. +
      +

      +
      max_depth
      +
      +
      +Defaults to 1000, this option controls how deep a directory structure find +will traverse. Meant mainly as a safety, and should not need to be adjusted. +
      +

      +

      +

      +

      move

      +

      move has the same syntax as the system mv command:

      +
      +    move 'file', 'file2';
      +    move 'file1', 'file2', 'dir';
      +    move 'file1', 'file2', 'dir3', 'dir';
      +    move '*.c', 'dir', { globbing => 1 };
      +

      The only difference is the last argument can be a hash ref of options. The +following options are allowed:

      +
      +
      globbing
      +
      +
      error_handler
      +
      +
      max_depth
      +
      +
      +

      +

      +

      del

      +

      del has the same syntax as the rm system command, but it can not remove +directories. Use deldir below to recursively remove files.

      +
      +    del 'file1';
      +    del '*.c', { globbing => 1 };
      +    del 'a', 'b', 'c';
      +

      It takes a list of files or directories to delete, and an optional hash ref +of options. The following options are allowed:

      +
      +
      error_handler
      +
      +
      globbing
      +
      +
      +

      +

      +

      deldir

      +

      deldir is similiar to del, but allows recursive deletes of directories:

      +
      +    deldir 'file1';
      +    deldir 'dir11', 'dir2', 'dir3';
      +    deldir '/home/a*', { globbing => 1 };
      +

      It takes a list of files and/or directories to remove, and an optional hash ref +of options. The following options are allowed:

      +
      +
      error_handler
      +
      +
      globbing
      +
      +
      max_depth
      +
      +
      +

      +

      +

      copy

      +

      copy is similiar to the system cp command:

      +
      +    copy 'file1', 'file2';
      +    copy 'file1', 'file2', 'file3', 'dir1';
      +    copy '*.c', '/usr/local/src', { globbing => 1 };
      +    copy
      +

      It copies a source file to a destination file or directory. You can also +specify multiple source files, and copy them into a single directory. The +last argument should be a hash ref of options:

      +
      +
      set_perms
      +
      +
      +This option will preserve permissions. i.e.: if the original file is set 755, +the copy will also be set 755. It defaults on. +
      +

      +
      set_owner
      +
      +
      +This option will preserver file ownership. Note: you must be root to be able +to change ownerhsip of a file. This defaults off. +
      +

      +
      set_time
      +
      +
      +This option will preserve file modification time. +
      +

      +
      preserve_all
      +
      +
      +This option sets set_perms, set_owner and set_time on. +
      +

      +
      error_handler
      +
      +
      globbing
      +
      +
      max_depth
      +
      +
      +

      +

      +

      mkpath

      +

      mkpath recursively makes a directory. It takes the same arguments as +perl's mkdir():

      +
      +    mkpath("/home/alex/create/these/dirs", 0755) or die "Can't mkpath: $!";
      +

      For compatibility with older module versions, rmkdir() is an alias for +mkpath().

      +

      +

      +

      parsefile

      +

      This function takes any type of filename (relative, fullpath, etc) and +returns the inputs directory, file, and whether it is a relative path or +not. For example:

      +
      +    my ($directory, $file, $is_relative) = parsefile("../foo/bar.txt");
      +

      +

      +

      dirname

      +

      Returns the directory portion of a filename.

      +

      +

      +

      basename

      +

      Returns the last portion of a filename (typically, the filename itself without +any leading directory). A deprecated filename() alias for basename() also +exists.

      +

      +

      +

      expand

      +

      Uses shell like expansion to expand a list of filenames to full paths. For +example:

      +
      +    my @source   = expand("*.c", "*.h");
      +    my @homedirs = expand("/home/*");
      +

      If you pass in relative paths, expand always returns absolute paths of +expanded files. Note: this does not actually go to the shell.

      +

      +

      +
      +

      SEE ALSO

      +

      This module depends on perl's Cwd module for getting the current working +directory. It also uses GT::AutoLoader to load on demand functions.

      +

      +

      +
      +

      MAINTAINER

      +

      Scott Beck

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Tools.pm,v 1.61 2005/05/13 01:48:23 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/FileMan.html b/site/glist/templates/help/GT/FileMan.html new file mode 100644 index 0000000..02d02e7 --- /dev/null +++ b/site/glist/templates/help/GT/FileMan.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/FileMan.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/FileMan/Commands.html b/site/glist/templates/help/GT/FileMan/Commands.html new file mode 100644 index 0000000..7ff3e0b --- /dev/null +++ b/site/glist/templates/help/GT/FileMan/Commands.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/FileMan/Commands.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/FileMan/Diff.html b/site/glist/templates/help/GT/FileMan/Diff.html new file mode 100644 index 0000000..f701ad8 --- /dev/null +++ b/site/glist/templates/help/GT/FileMan/Diff.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/FileMan/Diff.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/Installer.html b/site/glist/templates/help/GT/Installer.html new file mode 100644 index 0000000..3a4ffff --- /dev/null +++ b/site/glist/templates/help/GT/Installer.html @@ -0,0 +1,713 @@ + + + +GT::Installer - Performs initial installs for GTI products + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Installer - Performs initial installs for GTI products

      +

      +

      +
      +

      SYNOPSIS

      +
      +    main();
      +
      +    sub main {
      +        my $format = 'scroll1';
      +        my $installer = GT::Installer->new(
      +            product        => 'Gossamer Mail',
      +            version        => '2.0.0 beta 1',
      +            load_defaults  => \&load_defaults,
      +            load_config    => \&load_config,
      +            save_config    => \&save_config,
      +            checksums      => "<%Data Path%>/admin/checksums",
      +            welcome_format => $format
      +        );
      +
      +        $installer->add_config_message(q|
      +    This should be the system path and url (start with http://) 
      +    to the directory where your admin files are. No trailing 
      +    slash please.|, $format);
      +
      +        $installer->add_config(
      +            type     => 'path',
      +            key      => "Admin Path",
      +            message  => 'Admin Path',
      +        );
      +    }
      +    # Regex to path keys. These regexs have to capture
      +    # the file in $1
      +        $installer->install_to(
      +            '^admin/(.*)$'  => 'Admin Path',
      +            '^user/(.*)$'   => 'User Path',
      +            '^batch/(.*)$'  => 'Batch Path',
      +            '^data/(.*)$'   => 'Data Path',
      +            '^images/(.*)$' => 'Images Path',
      +        );
      +        
      +        $installer->add_upgrade(
      +            message         => 'Program Files. e.g. .pm, .pl and .cgi files',
      +            file_list       => '\.(?:pl|cgi|pm)$'
      +        );
      +        $installer->add_upgrade(skip => 'ConfigData\.pm$');
      +        $installer->perform;
      +    }
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::Installer is an installer class for all Gossamer Threads +products. It will handle both a command line interface and a +CGI inteface.

      +

      All intallation directives are specified at the start and are +called for each CGI request if being ran in CGI mode.

      +

      +

      +

      Creating a new GT::Installer object

      +

      There are several options when creating a new GT::Installer +object. All options are in the form of key value pairs. The +options can be passed in as a flattened hash or as a hash +reference.

      +

      There are two options that must be specified. These are +product and version. All other options are optional.

      +

      There are two ways to get a GT::SQL object. First, you can simply +provide the path to the def file directory where GT::SQL stores all +it's information:

      +
      +    $db = new GT::SQL '/path/to/def';
      +

      or you can pass in a hash or hash ref and specify options:

      +
      +    $db = new GT::SQL {
      +                        def_path => '/path/to/def',
      +                        cache    => 1,
      +                        debug    => 1,
      +                        subclass => 1
      +                    );
      +

      You must specify def_path. Setting cache => 1, will result in +all table or relation objects to be cached which can improve +performance.

      +

      Setting subclass => 0 or subclass => 1 will enable or disable +the ability to subclass any of the objects GT::SQL creates. The +default behaviour is 1.

      +

      GT::SQL has advanced debugging, and setting it to 1 should be +adequate for most operations.

      +
      +
      product
      +
      +
      +Specifies the name of the product. This name is used in the +welcome message and in various parts of the dialogue. +
      +
      +
      +    product => 'Gossamer Mail'
      +
      +

      +
      version
      +
      +
      +This is the version of the product. It is used on the startup +screen to tell the user what version we are installing or +upgrading to. +
      +
      +
      +    version => '2.0.0 beta 1'
      +
      +

      +
      load_defaults
      +
      +
      +This is a code reference that is called when installing. It +is used to defaults for prompts you have set up. The only +argument to this code reference if the installer object +which has method to assist in setting up defaults for the +different configuration options that you would specify. +
      +
      +

      If you return false from this code reference the error is +expected to be in GT::Installer::error. GT::Installer inherets +from GT::Base so you can just call the error method on the +object and return it to achieve this.

      +
      +
      +
      +    load_defaults => \&load_defaults
      +
      +

      +
      load_config
      +
      +
      +This is another code reference. It is called when the user +specifies that they are doing an upgrade. The argument to +this callback is the installer object. The path to the admin +directory of the last install is a key in the installer object +admin_path. In this code reference you will need to correlate +all the config option keys to the values in your config file. +See the example included in this pod for a possible why to +do this. +
      +
      +
      +    load_config => \&load_config
      +
      +
      +

      If you return false from this code reference the error is +expected to be in GT::Installer::error. GT::Installer inherets +from GT::Base so you can just call the error method on the +object and return it to achieve this.

      +
      +

      +
      save_config
      +
      +
      +This is a code reference that is called after an install or +upgrade. It gives you the opertunity to save any user input into +there new/old config file. +
      +
      +
      +    save_config => \&save_config
      +
      +

      +
      checksums
      +
      +
      +This should be set to the full system path to the checksum file. +If this is not set checksums will not be used, which makes +the upgrade questions usless and all files will be overridden. +There is no possible way you can know the full path to this file +at the point you set it. You can specify tags in this path +that will get replaced with what the user entered. The tags +are similar to the GT::Template manpage tags in that they start with +&lt;% and end with %&gt;. No other the GT::Template manpage tags conventions +are used. +
      +
      +
      +    checksums => "<%Data Path%>/admin/checksums"
      +
      +

      +
      welcome_format
      +
      +
      +This is the welcome format. There are named formats that are +used in GT::Installer for most command line output. This +is the one that is used for the initial message. This is not +used in CGI mode. +
      +
      +

      There are currently 4 formats (more to come :)). There are:

      +
      +
      +
      scroll1
      +
      +
      +
      +     _________________________________________________________________
      +    /\                                                                \
      +    \_|                                                                |
      +      |                                                                |
      +      | This is the scroll1 format                                     |
      +      |  ______________________________________________________________|_
      +      \_/_______________________________________________________________/
      +
      +
      scroll2
      +
      +
      +
      +     __^__                                                                __^__
      +    ( ___ )--------------------------------------------------------------( ___ )
      +     | / |                                                                | \ |
      +     | / | This is the scroll2 format                                     | \ |
      +     |___|                                                                |___|
      +    (_____)--------------------------------------------------------------(_____)
      +
      +
      professional
      +
      +
      +
      +     #================================================================#
      +     =                                                                =
      +     =  This is the professional format                               =
      +     #================================================================#
      +
      +
      none
      +
      +
      +This format just performs line wraps. Has no outline :( +
      +

      +
      +

      +

      +

      add_config - Adding configuration options.

      +

      There are 3 methods for adding user prompts. When I say user promts I +mean that in the telnet sence, in CGI mode it is just a table row.

      +

      This is the method you will be calling for every install option the +user should specify. This method takes it's arguments as key value +pair. The arguments can either be in the form of a flatened hash or +a hash reference.

      +
      +    $installer->add_config(
      +        type            => 'url',
      +        key             => "Admin URL",
      +        message         => 'Admin URL',
      +        telnet_callback => \&telnet_callback,
      +    );
      +

      Each key in the hash defines an attribute of the user prompt.

      +
      +
      type
      +
      +
      +The type attribute should be one of the built in input types. There is +currently no way to specify your own type. If this becomes a problem +it will be added. The built in types are as follows. +
      +
      +
      +    url           - User specifies a URL.
      +    ftp           - User specifies an FTP URL.
      +    path          - User specifies a Path to something on the system.
      +    message       - This is the same as calling the add_config_message 
      +                    method.
      +    create_dirs   - This is a yes/no answer on weather the user wants the
      +                    directories for this install created.
      +    email         - User specifies and email address. This is commonly 
      +                    used to prompt for the admin email address.
      +    reg_number    - User specifies the registration number they recieved
      +                    from us when they paid for the product.
      +    email_support - Specify either the path to sendmail or the hostname
      +                    of an smtp server. What is specified with be in the 
      +                    config hash as email_support. The key default for
      +                    this option is Mailer.
      +    perl_path     - Specify the path to perl on this system.
      +
      +

      +
      key
      +
      +
      +Each item the user enters is stored in a hash in the installer object. +This hash is called config. This specifies the key used in that hash +to store this user option. +
      +
      +

      This key is also used at the end of the telnet install to display +the options the user has specified for the install.

      +
      +
      +

      You will be accessing these keys in your configuration callbacks to +either set defaults or save the user specified options in your config +file. See the complete example below to see how this is used.

      +
      +

      +
      message
      +
      +
      +This is the message the user is pompted with in telnet. From the with +this appears on the left of the form the user fills out. +
      +
      +

      This will default to the value of the key if not specified

      +
      +

      +
      telnet_callback
      +
      +
      +This is a code reference that, if specified, will be ran after the user +enteres the information. You can use this to tweek the other option +defaults. If this method returns false the user will be reprompted for +the information. So you can effectivly use this to validate command +line input. +
      +

      +

      +

      +

      add_config_message - Add a configuration message.

      +

      This is a method to add a configuration message. This message is +displayed to the user in telnet in the order you specify it. No prompt +is performed for these messages.

      +

      This is a shortcut function that is the same as specifing:

      +
      +    $installer->add_config(
      +        type    => 'message',
      +        message => 'This message is displayed to the user',
      +        format  => 'none'
      +    );
      +

      The arguments to this function are (message, format). Message is what +is displayed, format is the format used. See above for a list of +formats.

      +
      +    $installer->add_config_message(q|My configuration message.|, 'professional');
      +

      +

      +

      install_exit_message - Add an exit message for installs.

      +

      This is the message that is displayed after the installation is +complete. This message uses the same convention for tags as the +checksum option for the constructor method. Any keys that are set +during the install will be available as tags here. The second argument +to this method is an optional format (used in telnet see above).

      +
      +    $installer->install_exit_message(q|
      +    To run the setup, point your browser to: 
      +    <a href="<%Admin URL%>/admin.cgi"><%Admin URL%>/admin.cgi</a>
      +    |, 'scroll2');
      +

      +

      +

      install_to - Specify where to untar files to.

      +

      The way this options is specified is a bit strange and my be rewritten. +It takes it arguments as a hash of regular expressions to keys. The +keys are the keys you specified with add_config(). The regexs are matched +against the relative path in the tar file. Anything captured in $1 is +appended to the value the user entered for that regexs key. For example +is you specify a key Admin Path such as

      +
      +    $installer->add_config(
      +        type  => 'path',
      +        key   => 'Admin Path'
      +    );
      +

      You could then use the key like:

      +
      +    $installer->install_to(
      +        '^admin/(.*)' => 'Admin Path'
      +    );
      +

      This would replace admin/ in the relative path in the tar file with what +the user entered for the Admin Path prompt.

      +

      +

      +

      use_lib - Set the use lib path for addition to all .pl and .cgi files.

      +

      The argument to this should be a key specified with add_config(). The +path the user entered for that config is added to all .cgi and .pl files +in a use lib '' statement. Followig the example above:

      +
      +    $installer->use_lib('Admin Path');
      +

      All .cgi and .pl file in the install will now have

      +
      +    use lib '/home/bline/projects/library';
      +

      added to them assuming the path the user entered for that config option was +/home/bline/projects/library'.

      +

      You can set $GT::Installer::USE_LIB_SPACES to something other than the default +4 spaces to alter the number of spaces that will be put before the ``use lib''. +Please be careful - if you set this to something other than whitespace you are +asking for trouble or being an 1337 h4xx0r.

      +

      +

      +

      replace_path - Generic method to replace paths upon install

      +

      The argument should be a hash of key => value replacements that should be +made upon installation.

      +
      +    $installer->replace_path(
      +        '../private/ConfigData.pm' => '<%Private_Path%>/ConfigData.pm'
      +    );
      +

      This will replace all occurrences of ../private/ConfigData.pm with what the +user entered in <%Private_Path%>.

      +

      +

      +

      add_upgrade - Adding upgrade options.

      +

      This is a method for grouping files and or directories under user prompted +upgrade options. This is NOT designed to be a complete upgrade system. It +handled basic checksuming, overwrites and backup. This should probably not +be used for a major upgrade.

      +

      more to come...

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Installer.pm,v 1.93 2005/03/27 20:52:53 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/MD5.html b/site/glist/templates/help/GT/MD5.html new file mode 100644 index 0000000..8f4867e --- /dev/null +++ b/site/glist/templates/help/GT/MD5.html @@ -0,0 +1,472 @@ + + + +GT::MD5 - Perl implementation of Ron Rivests MD5 Algorithm + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::MD5 - Perl implementation of Ron Rivests MD5 Algorithm

      +

      +

      +
      +

      DISCLAIMER

      +

      Majority of this module's code is borrowed from Digest::Perl::MD5 (Version 1.8).

      +

      This is not an interface (like Digest::MD5) but a Perl implementation of MD5. +It is written in perl only and because of this it is slow but it works without C-Code. +You should use Digest::MD5 instead of this module if it is available. +This module is only usefull for

      +
      +
      +
      +computers where you cannot install Digest::MD5 (e.g. lack of a C-Compiler) +
      +

      +
      +
      +encrypting only small amounts of data (less than one million bytes). I use it to +hash passwords. +
      +

      +
      +
      +educational purposes +
      +

      +

      +

      +
      +

      SYNOPSIS

      +
      + # Functional style
      + use Digest::MD5  qw(md5 md5_hex md5_base64);
      +
      + $hash = md5 $data;
      + $hash = md5_hex $data;
      + $hash = md5_base64 $data;
      +
      + # OO style
      + use Digest::MD5;
      +
      + $ctx = Digest::MD5->new;
      +
      + $ctx->add($data);
      + $ctx->addfile(*FILE);
      +
      + $digest = $ctx->digest;
      + $digest = $ctx->hexdigest;
      + $digest = $ctx->b64digest;
      +

      +

      +
      +

      DESCRIPTION

      +

      This modules has the same interface as the much faster Digest::MD5. So you can +easily exchange them, e.g.

      +
      +        BEGIN {
      +          eval {
      +            require Digest::MD5;
      +            import Digest::MD5 'md5_hex'
      +          };
      +          if ($@) { # ups, no Digest::MD5
      +            require Digest::Perl::MD5;
      +            import Digest::Perl::MD5 'md5_hex'
      +          }             
      +        }
      +

      If the Digest::MD5 module is available it is used and if not you take +Digest::Perl::MD5.

      +

      You can also install the Perl part of Digest::MD5 together with Digest::Perl::MD5 +and use Digest::MD5 as normal, it falls back to Digest::Perl::MD5 if it +cannot load its object files.

      +

      For a detailed Documentation see the Digest::MD5 module.

      +

      +

      +
      +

      EXAMPLES

      +

      The simplest way to use this library is to import the md5_hex() +function (or one of its cousins):

      +
      +    use Digest::Perl::MD5 'md5_hex';
      +    print 'Digest is ', md5_hex('foobarbaz'), "\n";
      +

      The above example would print out the message

      +
      +    Digest is 6df23dc03f9b54cc38a0fc1483df6e21
      +

      provided that the implementation is working correctly. The same +checksum can also be calculated in OO style:

      +
      +    use Digest::MD5;
      +    
      +    $md5 = Digest::MD5->new;
      +    $md5->add('foo', 'bar');
      +    $md5->add('baz');
      +    $digest = $md5->hexdigest;
      +    
      +    print "Digest is $digest\n";
      +

      The digest methods are destructive. That means you can only call them +once and the $md5 objects is reset after use. You can make a copy with clone:

      +
      +        $md5->clone->hexdigest
      +

      +

      +
      +

      LIMITATIONS

      +

      This implementation of the MD5 algorithm has some limitations:

      +
      +
      +
      +It's slow, very slow. I've done my very best but Digest::MD5 is still about 100 times faster. +You can only encrypt Data up to one million bytes in an acceptable time. But it's very usefull +for encrypting small amounts of data like passwords. +
      +

      +
      +
      +You can only encrypt up to 2^32 bits = 512 MB on 32bit archs. But You should +use Digest::MD5 for those amounts of data anyway. +
      +

      +

      +

      +
      +

      SEE ALSO

      +

      the Digest::MD5 manpage

      +

      md5(1)

      +

      RFC 1321

      +

      tools/md5: a small BSD compatible md5 tool written in pure perl.

      +

      +

      +
      +

      COPYRIGHT

      +

      This library is free software; you can redistribute it and/or +modify it under the same terms as Perl itself.

      +
      + Copyright 2000 Christian Lackas, Imperia Software Solutions
      + Copyright 1998-1999 Gisle Aas.
      + Copyright 1995-1996 Neil Winton.
      + Copyright 1991-1992 RSA Data Security, Inc.
      +

      The MD5 algorithm is defined in RFC 1321. The basic C code +implementing the algorithm is derived from that in the RFC and is +covered by the following copyright:

      +
      +
      +
      +Copyright (C) 1991-1992, RSA Data Security, Inc. Created 1991. All +rights reserved. +
      +
      +

      License to copy and use this software is granted provided that it +is identified as the ``RSA Data Security, Inc. MD5 Message-Digest +Algorithm'' in all material mentioning or referencing this software +or this function.

      +
      +
      +

      License is also granted to make and use derivative works provided +that such works are identified as ``derived from the RSA Data +Security, Inc. MD5 Message-Digest Algorithm'' in all material +mentioning or referencing the derived work.

      +
      +
      +

      RSA Data Security, Inc. makes no representations concerning either +the merchantability of this software or the suitability of this +software for any particular purpose. It is provided ``as is'' +without express or implied warranty of any kind.

      +
      +
      +

      These notices must be retained in any copies of any part of this +documentation and/or software.

      +
      +

      +

      This copyright does not prohibit distribution of any version of Perl +containing this extension under the terms of the GNU or Artistic +licenses.

      +

      +

      +
      +

      AUTHORS

      +

      The original MD5 interface was written by Neil Winton +(<N.Winton (at) axion.bt.co.uk>).

      +

      Digest::MD5 was made by Gisle Aas <gisle (at) aas.no> (I took his Interface +and part of the documentation).

      +

      Thanks to Guido Flohr for his 'use integer'-hint.

      +

      This release was made by Christian Lackas <delta (at) lackas.net>.

      + + + + diff --git a/site/glist/templates/help/GT/MD5/Crypt.html b/site/glist/templates/help/GT/MD5/Crypt.html new file mode 100644 index 0000000..8a2097e --- /dev/null +++ b/site/glist/templates/help/GT/MD5/Crypt.html @@ -0,0 +1,311 @@ + + + +unix_md5_crypt - Provides interoperable MD5-based crypt function + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      unix_md5_crypt - Provides interoperable MD5-based crypt() function

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::MD5::Crypt;
      +
      +    $cryptedpassword = unix_md5_crypt($password, $salt);
      +
      +    $valid = $cryptedpassword eq unix_md5_crypt($password, $cryptedpassword);
      +

      +

      +
      +

      DESCRIPTION

      +

      the unix_md5_crypt() provides a crypt()-compatible interface to the +rather new MD5-based crypt() function found in modern operating systems. +It's based on the implementation found on FreeBSD 2.2.[56]-RELEASE and +contains the following license in it:

      +
      + "THE BEER-WARE LICENSE" (Revision 42):
      + <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
      + can do whatever you want with this stuff. If we meet some day, and you think
      + this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
      +

      apache_md5_crypt() provides a function compatible with Apache's +.htpasswd files. This was contributed by Bryan Hart <bryan@eai.com>. +As suggested by William A. Rowe, Jr. <wrowe@lnd.com>, it is +exported by default.

      + + + + diff --git a/site/glist/templates/help/GT/MIMETypes.html b/site/glist/templates/help/GT/MIMETypes.html new file mode 100644 index 0000000..da4df7b --- /dev/null +++ b/site/glist/templates/help/GT/MIMETypes.html @@ -0,0 +1,315 @@ + + + +GT::MIMETypes - Methods to guess MIME Types of files. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::MIMETypes - Methods to guess MIME Types of files.

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::MIMETypes;
      +
      +    my $file = '/foo/bar/abc.doc';
      +    my $mime = GT::MIMETypes::guess_type($file);
      +    my $img  = GT::MIMETypes::guess_image($file);
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::MIMETypes provides two simple methods guess_type and guess_image. +They take either a filename or a hash reference.

      +

      guess_type returns the MIME type of the file, and guess_image returns an +image name that represents the file.

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: MIMETypes.pm,v 1.24 2005/04/02 08:08:46 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/Mail.html b/site/glist/templates/help/GT/Mail.html new file mode 100644 index 0000000..3eedde6 --- /dev/null +++ b/site/glist/templates/help/GT/Mail.html @@ -0,0 +1,521 @@ + + + +GT::Mail - A simple interface to parsing, sending, and creating email. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Mail - A simple interface to parsing, sending, and creating email.

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::Mail;
      + 
      +    # Create and Sending
      +    GT::Mail->send(
      +        smtp      => 'gossamer-threads.com',
      +        smtp_port => 110, # optional; 110/465 (normal/SSL) will be used for the default
      +        smtp_ssl  => 1, # establish an SSL connection.  Requires Net::SSLeay 1.06 or newer.
      +        to        => 'scott@gossamer-threads.com',
      +        from      => 'scott@gossamer-threads.com',
      +        subject   => 'Hello!!',
      +        msg       => 'I am a text email'
      +    ) or die "Error: $GT::Mail::error";
      + 
      +    # Parsing and sending
      +    my $mail = GT::Mail->new(debug => 1);
      +
      +    # Parse an email that is in a file called mail.test
      +    my $parser = $mail->parse('mail.test') or die "Error: $GT::Mail::error"; 
      +     
      +    # Change who it is to
      +    $parser->set("to", 'scott@gossamer-threads.com');
      +
      +    # Add an attachment to it
      +    $mail->attach (
      +        type      => 'text/plain',
      +        encoding  => '-guess',
      +        body_path => 'Mail.pm',
      +        filename  => 'Mail.pm'
      +    );
      + 
      +    # Send the email we just parsed and modified
      +    $mail->send(sendmail => '/usr/sbin/sendmail') or die "Error: $GT::Mail::error";
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::Mail is a simple interface for parsing, creating, and sending email. It +uses GT::Mail::Send to send email and GT::Mail::Parse to parse and store email +data structurs. All the creation work is done from within GT::Mail.

      +

      +

      +

      Creating a new GT::Mail object

      +

      The arguments to new() in GT::Mail are mostly the same for all the class +methods in GT::Mail so I will be refering back to these further down. Mostly +these arguments are used to set parts of the header for creating an email. The +arguments can be passed in as either a hash or a hash ref. Any arguments aside +from these will be added to the content header as raw header fields. The +following is a list of the keys and a brief description.

      +
      +
      debug
      +
      +
      +Sets the debug level for this object. Anything but zero will produce ouput on +STDERR. +
      +

      +
      disposition
      +
      +
      +Sets the Content-Disposition. +
      +

      +
      filename
      +
      +
      +Sets the Content-Disposition to attachment and the file name to what to +specify. +
      +

      +
      encoding
      +
      +
      +Sets the Content-Transfer-Encoding (You really should not set this). +
      +

      +
      type
      +
      +
      +Sets the Content-Type. +
      +

      +
      body_data
      +
      +
      +Sets the top level body data to the in memory string specified. +
      +

      +
      msg
      +
      +
      +Same as body_data. +
      +

      +
      body_handle
      +
      +
      +Sets the top level body to the File Handle. +
      +

      +
      body_path
      +
      +
      +Sets the top level body path. +
      +

      +

      +

      +

      parser - Set or get the parse object.

      +
      +    my $parser = $mail->parser;
      +    $mail->parser($parser);
      +

      Set or get method for the parser object that is used when you call parse_head() +or parse(). This object must conform to the method parse and parse_head. If no +object is passed to this method a the GT::Mail::Parse manpage object is created when +needed.

      +

      +

      +

      parse - Parsing an email.

      +

      Instance method that returns a parts object. Emails are stored recursivly in +parts object. That is emails can have parts within parts within parts etc.. See +the GT::Mail::Parts manpage for details on the methods supported by the parts object +that is returned.

      +

      The parse() method takes only one argument. It can be a GLOB ref to a file +handle, a FileHandle object, or the path to a file. In any case the IO must +contain a valid formated email.

      +

      Once an email is parsed, you can make changes to it as you need and call the +send method to send it or call the write method to write it to file, etc.

      +

      This method will return false if an error occurs when parsing. The error +message will be set in $GT::Mail::error.

      +

      +

      +

      parse_head - Parsing just the head.

      +

      This method does the exact same thing as the parse method but it will only +parse the top level header of the email. Any IO's will be reset after the +parsing.

      +

      Use this method if whether you want to parse and decode the body of the email +depends on what is in the header of the email or if you only need access to the +header. None of the parts will contain a body.

      +

      +

      +

      send - Sending an email.

      +

      Class/Instance method for sending email. It sends the currently in memory +email. This means, if you parse an email, that email is in memory, if you +specify params for an email to new(), that is the email that gets sent. You can +also specify the params for the email to this method.

      +

      +

      +

      top_part - Getting a Parts object.

      +

      Instance method to set or get the top level part. If you are setting this, the +object must be from the GT::Mail::Parts manpage. You can use this to retrieve the part +object after you specify params to create an email. This object will contain +all the other parts for the email. e.g. attachments and emails that are +attached. See the GT::Mail::Parts manpage for more details on this object.

      +

      +

      +

      new_part - Creating a Parts object.

      +

      Instance method to get a new part object. This method takes the same arguments +as the new() constructor. Returns the new part object. The part object is +added to the current email only if arguments are given otherwize just returns +an empty part.

      +

      +

      +

      attach - Attaching to an email.

      +

      Instance method to attach to the in memory email. You can pass in a GT::Mail +object or you can pass the same arguments you would pass to new() to specify +all the information about the attachment. In addition if you specify a file +path and do not specify a mime type, this will attempt to guess the mime type +from the file extention.

      +

      +

      +

      to_string - Getting the email as a string.

      +

      Returns the entire email as a string. Do not use this function if you have +attachments and are worried about memory ussage.

      +

      +

      +

      as_string - Getting the email as a string.

      +

      Same as to_string.

      +

      +

      +

      build_email - Building an email.

      +

      Instance method that builds the currently in memory email. This method takes +one argument, a code ref. It calles the code ref with one argument. The code +ref is called for each section of the email that is created. A good example of +how to use this is what the as_string method does:

      +
      +    my $ret = '';
      +    $obj->build_email(sub { $ret .= $_[0] });
      +

      This puts the entire created email into the string $ret. You can use this, for +example to print the email to a filehandle (which is what the write() method +does).

      +

      +

      +

      write - Writing an email to a file handle.

      +

      Instance mothod that writes the currently in memory email to a file or file +handle. The only arguments this method takes is a file or a reference to a glob +that is a filehandle or FileHandle object.

      +

      +

      +

      naming - Setting the naming scheme.

      +

      Instance method to specify a naming scheme for parsing emails. Calling this +after the email is parsed has no effect. This method just wraps to the one in +the GT::Mail::Parse manpage.

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Mail.pm,v 1.70 2004/11/04 20:23:09 brewt Exp $

      + + + + diff --git a/site/glist/templates/help/GT/Mail/BulkMail.html b/site/glist/templates/help/GT/Mail/BulkMail.html new file mode 100644 index 0000000..d917c91 --- /dev/null +++ b/site/glist/templates/help/GT/Mail/BulkMail.html @@ -0,0 +1,536 @@ + + + +GT::Mail::BulkMail - A simplified interface to sending bulk emails + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Mail::BulkMail - A (perhaps overly) simplified interface to sending bulk emails

      +

      +

      +
      +

      SYNOPSIS

      +
      +    $mailer = new GT::Mail::BulkMail;
      +    $mailer->option("setting");
      +    $mailer->otheroption("othersetting");
      +    ...
      +    
      +    -- or --
      +
      +    $mailer = new GT::Mail::BulkMail(
      +        -option      => "setting",
      +        -otheroption => "othersetting",
      +        ...
      +    );
      +
      +    -- then --
      +
      +    sub subroutine {
      +        # Code to generate the next e-mail address
      +    }
      +    open FILE, "email_list.txt";
      +    %hash = ( 1 => 'some@fictional.address',
      +              2 => 'who@knows.where'
      +            );
      +    @array = ('yet@another.fictional.address','and@one.more');
      +    $mailer->send(\&subroutine,\*FILE,\%hash,\@array);
      +    close FILE;
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::Mail::BulkMail is a module to handle mass mailings. It is capable of +using either sendmail, or an SMTP server. It has the advantage of +not requiring multiple connections to the SMTP server.

      +

      +

      +
      +

      REQUIREMENTS

      +

      Perl 5.004

      +

      +

      +

      METHODS

      +

      All methods can be specified at object creation time as an option with the: +-option => value syntax. For example, $mailer = new GT::Mail::BulkMail(-from => "foo@bar.com") +would have the same effect as: $mailer = new GT::Mail::BulkMail(); $mailer->from("foo@bar.com")

      +
      +
      smtp
      +
      +
      +Sets the SMTP server to use, and sets the object mail sending method to use SMTP. Takes +SMTP server as argument. +
      +

      +
      sendmail
      +
      +
      +Sets the sendmail executable to use. Takes the path to sendmail as the argument. +
      +

      +
      text
      +
      +
      +Specifies that the mail format is text. This translates into Content-type: text/plain. +This is the default format. +
      +

      +
      html
      +
      +
      +Specifies that the mail format is HTML. (Content-type: text/html) +
      +

      +
      headers
      +
      +
      +Returns any custom headers set as a hash reference in scalar context, or a hash in list context. +
      +

      +
      add_header
      +
      +
      +Adds a single header. This can be any header starting with ``X-'' (Note that X-Mailer headers +will be prepended with the GT::Mail::BulkMail X-Mailer header (which includes the perl version, +OS name, GT::Mail::BulkMail module and CVS versions, and the Gossamer Threads homepage)). Pass +two arguments: A key (header name) and a value (header value). For example, for +X-Foo: blah blah blah you would use: $mailer->add_header(``X-Foo'' => ``blah blah blah'') +
      +

      +
      add_headers
      +
      +
      +Same as above, except it adds multiple headers. Has the same argument format. You would use: +$mailer->add_headers(``X-Foo1'' => ``blah'', ``X-Foo2'' => ``blah blah''); +
      +

      +
      delete_header
      +
      +
      +Deletes a single header. Pass the name of the header to delete. +
      +

      +
      delete_headers
      +
      +
      +Delete multiple headers. Pass a list of names of headers to delete. +
      +

      +
      from
      +
      +
      +Sets the ``from'' field of the e-mail. Must be set before $mailer->send() can be called. +Must be set to an e-mail address. If this e-mail address is rejected by the SMTP server, +no e-mails will be sent. +
      +

      +
      name
      +
      +
      +Sets the ``name'' field of the e-mail. This affects what is displayed in the ``From'' field. +When sending the email, the actual field will be set to: "This name" <some@name.net>. +Optional. +
      +

      +
      subject
      +
      +
      +Sets the subject of the message. If not specified, it will default to ``(no subject)'' +
      +

      +
      message
      +
      +
      +The body of the message. Can be left blank, but that seems rather pointless... +The message will be encoded using the quoted-printable format if it contains characters +outside the 7-bit range. +
      +

      +
      success
      +
      +
      +A code reference to be run for each and every successful e-mail sending. +Each call to this code reference will be given the e-mail address as the only argument +(unless using a message ID, which is discussed below). Optional. +
      +

      +
      failure
      +
      +
      +A code reference that will be run for any email addresses that cannot be sent. Each +call to this code reference will be given the ID or e-mail address as the argument +(message IDs are discussed below). Optional. +
      +

      +
      frompresend
      +
      +
      namepresend
      +
      +
      subjectpresend
      +
      +
      messagepresend
      +
      +
      +A code reference that will be run before sending an e-mail. The 'from', 'name', 'subject', +or 'message' field will be sent to the code references (depending on which method called) +and whatever is returned will be used as the actual field for the email sent. This can be +used to parse fields to customize them for each recipient. The subroutine is called with +two arguments: (ID_OR_EMAIL, FIELD). If an ID is provided, it will be passed as the +first argument, otherwise the email address will be passed. The second argument is the +field itself. The field used in the actual email to the user will be the value returned by +the subroutine. +
      +
      +

      The default field (for the rest of the mailing) can be changed by directly modifying $_[1] +itself.

      +
      +
      +

      If the subroutine reference returns an undefined value, the mailer will use the actual field +instead. You can use this technique to only modify some messages, but not others.

      +
      +
      +

      Optional.

      +
      +

      +
      show_errors
      +
      +
      +If set to something true it will warn() on all errors. Optional. The default is turned on, +but can easily be changed by modifying the line ' +
      +

      +
      error_code
      +
      +
      +Takes a code reference - the code reference will be called with the error as the argument +when an error occurs. Optional. +
      +

      +
      send
      +
      +
      +Takes any number of the following arguments: +
      +
      +
      array reference
      +
      +
      +An array reference of a list of e-mail addresses to send to. After each message, either the +success or failure callback will be called with the e-mail address as the argument, and +possibly a message as the second argument. +
      +

      +
      hash reference
      +
      +
      +A hash reference of ID => email pairs. For example, +123 => 'someone@whoknows.com'. The value will be used as the e-mail address to send +to, and the key will be an identifier to pass into the success or failure callbacks. +
      +

      +
      glob reference
      +
      +
      +A glob reference to an open file. Make sure the file is opened before passing this! +The file should contain one e-mail address per line. +
      +

      +
      subroutine or code reference
      +
      +
      +You may pass a code reference, and it will be called for each e-mail address. It is +assumed that the subroutine will return one e-mail address each time called, and +that a return value of ``undef'' indicates that there are no more e-mail addresses. +The code reference could alternatively return two items - if it does, it is assumed that +the first is an ID code, and that the second is the email address. When a call to either +or the success or failure callbacks, the ID will be provided as the first argument +instead of the e-mail address itself. +
      +
      +

      One cool feature to note about using code refs is that you can call next() from within +the code reference and it will then recall the code reference for the next value.

      +
      +
      +
      + + + + diff --git a/site/glist/templates/help/GT/Mail/Editor.html b/site/glist/templates/help/GT/Mail/Editor.html new file mode 100644 index 0000000..65a6df7 --- /dev/null +++ b/site/glist/templates/help/GT/Mail/Editor.html @@ -0,0 +1,454 @@ + + + +GT::Mail::Editor - E-mail template editor + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Mail::Editor - E-mail template editor

      +

      +

      +
      +

      SYNOPSIS

      +

      Generally used from templates:

      +
      +    <%GT::Mail::Editor::tpl_load(
      +        dir => $template_root,
      +        template => $template_set,
      +        file => $filename,
      +        header => From,
      +        header => To,
      +        header => Subject
      +    )%>
      +
      +    <%if error%>
      +        Unable to load e-mail template: <%error%>
      +    <%else%>
      +        From: <input type=text name=header_From value="<%header_From%>">
      +        To: <input type=text name=header_To value="<%header_To%>">
      +        Subject: <input type=text name=header_Subject value="<%header_Subject%>">
      +        Other headers:<br>
      +        <textarea name=extra_headers>
      +        <%loop extra_headers%><%name%>: <%value%>
      +        <%endloop%>
      +    <%endif%>
      +
      +    - or -
      +
      +    <%GT::Mail::Editor::save(
      +        dir => $template_root,
      +        template => $template_set,
      +        file => $filename,
      +        header => To => $header_To,
      +        header => From => $header_From,
      +        header => Subject => $header_Subject,
      +        extra_headers => $extra_headers
      +    )%>
      +    <%if error%>Unable to save e-mail template: <%error%>
      +        ... Display the above form in here ...
      +    <%endif%>
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::Mail::Editor is designed to provide a template interface to creating and +editing a wide variety of e-mail templates. Although not currently supported, +eventually attachments, HTML, etc. will be supported.

      +

      +

      +

      tpl_load - Loads a template (from the templates)

      +

      Calling GT::Mail::Editor::tpl_load from a template returns variables required to +display a form to edit the template passed in.

      +
      +
      dir
      +
      +
      +Defines the base directory of templates. +
      +

      +
      template
      +
      +
      +This defines a template set. This is optional. If present, this directory will +be tacked onto the end of 'dir'. This is simply to provide a more flexible way +to specify the template directory. For example, if you have 'dir' set to '/a/b' +and template set to 'c', then the directory '/a/b/c' will be used to save and +load e-mail templates. +
      +

      +
      file
      +
      +
      +Specify the filename of the template inside the directory already specified with +'dir' and 'template' +
      +

      +
      header
      +
      +
      +Multiple ``special'' headers can be requested with this. The argument following +each 'header' should be the name of a header, such as ``To''. Then, in the +variables returned from tpl_load(), you will have a variable such as 'header_To' +available, containing the value of the To: field. +
      +

      +

      +

      +

      tpl_save - Save a template

      +
      +
      dir template file
      +
      +
      +See the entries in tpl_load +
      +

      +
      header
      +
      +
      +Specifies that the two following arguments are the field and value of a header +field. For example, header => To => ``abc@example.com'' would specify that the To +field should be ``abc@example.com'' (To: abc@example.com). +
      +

      +
      extra_headers
      +
      +
      +The value to extra_headers should be a newline-delimited list of headers other +than those specified with header. These will be parsed, and blank lines skipped. +
      +

      +
      body
      +
      +
      +The body of the message. Need I say more? MIME messages are possible by +inserting them directly into the body, however currently MIME messages cannot +be created using this editor. +
      +

      +

      +

      +

      load

      +

      Attempts to load a GT::Mail::Editor object with data passed in. This can take +either a file handle or a filename. If passing a filename, dir and template +will be used (if available). You should construct an object with new() prior +to calling this method.

      +

      +

      +

      new

      +

      Constructs a new GT::Mail::Editor object. This will be done automatically when +using the template methods tpl_load and tpl_save. Takes the following +arguments:

      +
      +
      dir
      +
      +
      +Defines the base directory of templates. +
      +

      +
      template
      +
      +
      +This defines a template set. This is optional. If present, this directory will +be tacked onto the end of 'dir'. This is simply to provide a more flexible way +to specify the template directory. For example, if you have 'dir' set to '/a/b' +and template set to 'c', then the directory '/a/b/c' will be used to save and +load e-mail templates. +
      +

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Editor.pm,v 1.24 2005/01/18 23:06:40 bao Exp $

      + + + + diff --git a/site/glist/templates/help/GT/Mail/Editor/HTML.html b/site/glist/templates/help/GT/Mail/Editor/HTML.html new file mode 100644 index 0000000..7dbbb3f --- /dev/null +++ b/site/glist/templates/help/GT/Mail/Editor/HTML.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/Mail/Editor/HTML.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/Mail/Editor/Text.html b/site/glist/templates/help/GT/Mail/Editor/Text.html new file mode 100644 index 0000000..b227c36 --- /dev/null +++ b/site/glist/templates/help/GT/Mail/Editor/Text.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/Mail/Editor/Text.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/Mail/Encoder.html b/site/glist/templates/help/GT/Mail/Encoder.html new file mode 100644 index 0000000..9cd1fd7 --- /dev/null +++ b/site/glist/templates/help/GT/Mail/Encoder.html @@ -0,0 +1,364 @@ + + + +GT::Mail::Encoder - MIME Encoder + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Mail::Encoder - MIME Encoder

      +

      +

      +
      +

      SYNOPSIS

      +
      +        open IN, 'decoded.txt' or die $!;
      +        open OUT, '>encoded.txt' or die $!;
      +        if (GT::Mail::Encoder->supported ('7bit')) {
      +            GT::Mail::Encoder->decode (
      +                                    debug    => 1,
      +                                    encoding => '7bit',
      +                                    in       => \*IN,
      +                                    out      => sub { print OUT $_[0] }
      +                                ) or die $GT::Mail::Encoder::error;
      +        }
      +        else {
      +            die "Unsupported encoding";
      +        }
      +        close IN;
      +        close OUT;
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::Mail::Encoder is a MIME Encoder implemented in perl. It will try to use +the C extension for encoding Base64. If the extension is not there +it will do it in perl (slow!).

      +

      +

      +

      Encoding a stream

      +

      The new() constructor and the supported() class method are the only methods that +are public in the interface. The new() constructor takes a hash of params. +The supported() method takes a single string, the name of the encoding you want +to encode and returns true if the encoding is supported and false otherwise.

      +
      +
      debug
      +
      +
      +Set debugging level. 1 or 0. +
      +

      +
      encoding
      +
      +
      +Sets the encoding used to encode. +
      +

      +
      in
      +
      +
      +Set to a file handle or IO handle. +
      +

      +
      out
      +
      +
      +Set to a code reference, the decoded stream will be passed in at the first +argument for each chunk encoded. +
      +

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Encoder.pm,v 1.40 2004/01/13 01:35:17 jagerman Exp $ + +

      + + + + diff --git a/site/glist/templates/help/GT/Mail/Message.html b/site/glist/templates/help/GT/Mail/Message.html new file mode 100644 index 0000000..64dc75d --- /dev/null +++ b/site/glist/templates/help/GT/Mail/Message.html @@ -0,0 +1,399 @@ + + + +GT::Mail::Message - Encapsolates an email message. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Mail::Message - Encapsolates an email message.

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::Mail::Message;
      + 
      +    # Get a GT::Mail::Message object from the parser
      +    use GT::Mail::Parse;
      +
      +    my $parser = new GT::Mail::Parse( in_file => "myemail.eml" );
      +    my $message = $parser->parse;
      +
      +    # Get the top level part
      +    my $root_part = $message->root_part;
      +
      +    # Replace the first part with a new part
      +    $message->replace_part( $root_part, $message->new_part(
      +        to => 'scott@gossamer-threads.com',
      +        from => 'alex@gossamer-threads.com',
      +        'content-type' => 'text/plain',
      +        body_data => 'Hi Scott, how are you?!'
      +    );
      +
      +    # Add a part at the end
      +    my $end_part = $message->new_part(
      +        'content-type' => 'image/gif',
      +        body_path      => 'myimage.jpg'
      +    );
      +    $message->add_part_end( $root_part, $end_part );
      +
      +    # Move the first part in the top part to after the end part
      +    $message->move_part_after( $root_part->parts->[0], $end_part );
      +
      +    # Print the mime message
      +    print $message->to_string;
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::Mail::Message encapsolates a mime message which consists of +the GT::Mail::Parts manpage object. This module provides methods to change, +move, remove, and access these parts.

      +

      +

      +

      Creating a new GT::Mail::Message object

      +

      Usually you will get a GT::Mail::Message object by call the parse method +in the GT::Mail::Parse manpage.

      +
      +    my $message = $parser->parse;
      +

      You may also call new on this class specifying the top level part and or +a debug level.

      +
      +    my $message = new GT::Mail::Message(
      +        root_part => $part,
      +        debug    => 1
      +    );
      +

      +

      +

      Creating a new Part

      +

      You can create a part by calling new on the GT::Mail::Parts manpage directly

      +
      +    my $part = new GT::Mail::Parts;
      +    $part->set( 'content-type' => 'image/gif' );
      +    $part->body_path( 'myimage.gif' );
      +

      or you can call a method in this module to get a new part

      +
      +    my $part = $message->new_part(
      +        'content-type' => 'image/gif',
      +        body_path      => 'myimage.gif'
      +    );
      +

      This method is a wraper on a combination of new() and some other +supporting methods in the GT::Mail::Parts manpage such as body_path(). Anything +that is not body_path, body_data, or body_handle is treated +as header values.

      +

      +

      +

      Manipulating Parts

      +

      A MIME message is just a format for storing a tree structure. We provide +tree-like methods to manipulate parts. All the method for manipulating +parts take the part object(s) as arguments. We do this so you do not need +to know how the tree is tracked internally.

      +

      +

      +

      Accessing Parts

      +

      More to come!

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/ + +

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Message.pm,v 1.14 2004/01/13 01:35:17 jagerman Exp $ + +

      +
      +        
      +
      +
      + + + + diff --git a/site/glist/templates/help/GT/Mail/POP3.html b/site/glist/templates/help/GT/Mail/POP3.html new file mode 100644 index 0000000..f2394f1 --- /dev/null +++ b/site/glist/templates/help/GT/Mail/POP3.html @@ -0,0 +1,594 @@ + + + +GT::Mail::POP3 - Receieve email through POP3 protocal + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Mail::POP3 - Receieve email through POP3 protocal

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::Mail::POP3;
      +
      +    my $pop = GT::Mail::POP3->new(
      +        host      => 'mail.gossamer-threads.com',
      +        port      => 110,
      +        user      => 'someusername',
      +        pass      => 'somepassword',
      +        auth_mode => 'PASS',
      +        timeout   => 30,
      +        debug     => 1
      +    );
      +
      +    my $count = $pop->connect or die $GT::Mail::POP3::error;
      +
      +    for my $num (1 .. $count) {
      +        my $top = $pop->parse_head($num);
      +
      +        my @to = $top->split_field;
      +
      +        if (grep /myfriend\@gossamer-threads\.com/, @to) {
      +            $pop->message_save($num, '/keep/email.txt');
      +            last;
      +        }
      +    }
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::Mail::POP3 is a module to check an email account using the POP3 protocol. +Many of the methods are integrated with the GT::Mail::Parse manpage.

      +

      +

      +

      new - constructor method

      +

      This method is inherited from the GT::Base manpage. The argument to this method can be +in the form of a hash or hash ref. As a minimum 'user', 'pass', and 'host' must +be specified.

      +
      +
      debug
      +
      +
      +Sets the debugging level for this instance of GT::Mail::POP3. +
      +

      +
      host
      +
      +
      +Sets the host to connect to for checking a POP account. This argument must be +provided. +
      +

      +
      port
      +
      +
      +Sets the port on the POP server to attempt to connect to. This defaults to 110, +unless using SSL, for which the default is 995. +
      +

      +
      ssl
      +
      +
      +Establishes the connection using SSL. Note that this requires Net::SSLeay of +at least version 1.06. +
      +

      +
      user
      +
      +
      +Sets the user name to login with when connecting to the POP server. This must +be specified. +
      +

      +
      pass
      +
      +
      +Sets the password to login with when connection to the POP server. This must be +specified. +
      +

      +
      auth_mode
      +
      +
      +Sets the authentication type for this connection. This can be one of two +values. PASS (the default) or APOP. If set to APOP, GT::Mail::POP3 will use +APOP to login to the remote server. +
      +

      +
      timeout
      +
      +
      +Sets the connection timeout. This isn't entirely reliable as it uses alarm(), +which isn't supported on all systems. That aside, this normally isn't needed +if you want a timeout - it defaults to 30 on alarm()-supporting systems. The +main purpose is to provide a value of 0 to disable the alarm() timeout. +
      +

      +

      +

      +

      connect - Connect to the POP account

      +
      +    $obj->connect or die $GT::Mail::POP3::error;
      +

      This method performs the connection to the POP server. Returns the count of +messages on the server on success, and undefined on failure. Takes no arguments +and called before you can perform any actions on the POP server.

      +

      +

      +

      head_part - Access the email header

      +
      +    # Get a parsed header part object for the first email in the list.
      +    my $top_part = $obj->head_part(1);
      +

      Instance method. The only argument to this method is the message number to get. +Returns a the GT::Mail::Parts manpage object containing only the parsed header of the +specified message.

      +

      +

      +

      all_head_parts - Access all email headers

      +
      +    # Get all the head parts from all messages
      +    my @headers = $obj->all_head_parts;
      +

      Instance method. Gets all the headers of all the email's on the remote server. +Returns an array of the the GT::Mail::Parts manpage object. One object for each +email. None of the email's bodies are retrieved, only the head.

      +

      +

      +

      parse_message - Access an email

      +
      +    # Parse an email and get the GT::Mail object
      +    my $mail = $obj->parse_message (1);
      +

      Instance method. Pass in the number of the email to retrieve. This method +retrieves the specified email and returns the parsed GT::Mail object. If this +method fails you should check $GT::Mail::error for the error message.

      +

      +

      +

      message_save - Save an email

      +
      +    open FH, '/path/to/email.txt' or die $!;
      +
      +    # Save message 2 to file
      +    $obj->message_save (2, \*FH);
      +    close FH;
      +

      - or -

      +
      +    $obj->message_save (2, '/path/to/email.txt') or die $GT::Mail::POP3::error;
      +

      Instance method. This method takes the message number as it's first argument, +and either a file path or a file handle ref as it's second argument. If a file +path is provided the file will be opened to truncate. The email is then +retrieved from the server and written to the file.

      +

      +

      +

      stat - Do a STAT command

      +
      +    # Get the number of messages on the server
      +    my $count = $obj->stat;
      +

      Instance method. Does a STAT command on the remote server. It stores the total +size and returns the count of messages on the server, if successful. Otherwise +returns undef.

      +

      +

      +

      list - Do a LIST command

      +
      +    # At a list of messages on the server
      +    my @messages = $obj->list;
      +

      Instance method. Does a LIST command on the remote server. Returns an array of +the lines in list context and a single scalar that contains all the lines in +scalar context.

      +

      +

      +

      rset - Do an RSET command

      +
      +    # Tell the server to ignore any dele commands we have issued in this
      +    # session
      +    $obj->rset;
      +

      Instance method. Does an RSET command. This command resets the servers +knowledge of what should be deleted when QUIT is called. Returns 1 on success.

      +

      +

      +

      dele - Do a DELE command

      +
      +    # Delete message 4
      +    $obj->dele (4);
      +

      Instance method. Does a DELE command. The only argument is the message number +to delete. Returns 1 on success.

      +

      +

      +

      quit - Quit the connection

      +
      +    # Close our connection
      +    $obj->quit;
      +

      Instance method. Sends the QUIT command to the server. The should should +disconnect soon after this. No more actions can be taken on this connection +until connect is called again.

      +

      +

      +

      uidl - Do a UIDL command

      +
      +    # Get the uidl for message 1
      +    my $uidl = $obj->uidl (1);
      +
      +    # Get a list of all the uidl's and print them
      +    $obj->uidl (sub { print @_ });
      +
      +    # Get an array of all the uidl's
      +    my @uidl = $obj->uidl;
      +

      Instance method. Attempts to do a UIDL command on the remote server. Please be +aware support for the UIDL command is not very wide spread. This method can +take the message number as it's first argument. If the message number is given, +the UIDL for that message is returned. If the first argument is a code +reference, a UIDL command is done with no message specified and the code +reference is called for each line returned from the remote server. If no second +argument is given, a UIDL command is done, and the results are returned in a +has of message number to UIDL.

      +

      +

      +

      count - Get the number of messages

      +
      +    # Get the count from the last STAT
      +    my $count = $obj->count;
      +

      This method returns the number of messages on the server from the last STAT +command. A STAT is done on connect.

      +

      +

      +

      size - Get the size of all messages

      +
      +    # Get the total size of all messages on the server
      +    my $size = $obj->size;
      +

      This method returns the size of all messages in the server as returned by the +last STAT command sent to the server.

      +

      +

      +

      send - Send a raw command

      +
      +    # Send a raw command to the server
      +    my $ret = $obj->send ("HELO");
      +

      This method sends the specified raw command to the POP server. The one line +return from the server is returned. Do not call this method if you are +expecting more than a one line response.

      +

      +

      +

      top - Retrieve the header

      +
      +    # Get the header of message 2 in an array.  New lines are stripped
      +    my @header = $obj->top (2);
      +
      +    # Get the header as a string
      +    my $header = $obj->top (2);
      +

      Instance method to retrieve the top of an email on the POP server. The only +argument should be the message number to retrieve. Returns a scalar containing +the header in scalar context and an array, which is the scalar split on +\015?\012, in list context.

      +

      +

      +

      retr - Retrieve an email

      +
      +    # Get message 3 from the remote server in an array.  New lines are stripped
      +    my @email = $obj->retr (3);
      +
      +    # Get it as a string
      +    my $email = $obj->retr (3);
      +

      Instance method to retrieve an email from the POP server. The first argument to +this method should be the message number to retrieve. The second argument is an +optional code ref to call for each line of the message that is retrieved. If no +code ref is specified, this method will put the email in a scalar and return +the scalar in scalar context and return the scalar split on \015?\012 in list +context.

      +

      +

      +
      +

      REQUIREMENTS

      +

      the GT::Socket::Client manpage +the GT::Base manpage +the GT::MD5 manpage (for APOP authentication)

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: POP3.pm,v 1.56 2004/03/19 00:36:16 brewt Exp $

      + + + + diff --git a/site/glist/templates/help/GT/Mail/Parse.html b/site/glist/templates/help/GT/Mail/Parse.html new file mode 100644 index 0000000..e840f6d --- /dev/null +++ b/site/glist/templates/help/GT/Mail/Parse.html @@ -0,0 +1,420 @@ + + + +GT::Mail::Parse - MIME Parse + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Mail::Parse - MIME Parse

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::Mail::Parse
      +    
      +    my $parser = new GT::Mail::Parse (
      +        naming  => \&name_files,
      +        in_file => '/path/to/file.eml',
      +        debug   => 1
      +    );
      +
      +    my $top = $parser->parse or die $GT::Mail::Parse::error;
      +
      +    - or -
      +
      +    my $parser = new GT::Mail::Parse;
      +    
      +    open FH, '/path/to/file.eml' or die $!;
      +    my $top = $parser->parse (
      +        naming  => \&name_files,
      +        handle  => \*FH,
      +        debug   => 1
      +    ) or die $GT::Mail::Parse::error;
      +    close FH;
      +
      +    - or -
      +
      +    my $parser = new GT::Mail::Parse;
      +
      +    my $top_head = $parser->parse_head (
      +        naming  => \&name_files,
      +        in_file => '/path/to/file.eml',
      +        debug   => 1
      +    ) or die $GT::Mail::Parse::error;
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::Mail::Parse is a 100% rfc822 email MIME parser that supports unlimited +nested levels of MIME. Emails are parsed into the GT::Mail::Parts manpage objects. Each +part knows where it's body is and each part contains it's sub parts. See +the GT::Mail::Parts manpage for details on parts methods.

      +

      +

      +

      new - Constructor method

      +

      This is the constructor method to get a GT::Mail::Parse object, which you +need to access all the methods (there are no Class methods). new() takes +a hash or hash ref as it's arguments. Each key has an accessor method by the +same name except debug, which can only be set by passing debug to new(), parse() +or parse_head().

      +
      +
      debug
      +
      +
      +Sets the debug level for this insance of the class. +
      +

      +
      naming
      +
      +
      +Specify a code reference to use as a naming convention for each part of the +email being parsed. This is useful to keep file IO down when you want the emails +seperated into each part as a file. If this is not specified GT::Mail::Parse +uses a default naming, which is to start at one and incriment that number for each +attachment. The attachments would go in the current working directory. +
      +

      +
      in_file
      +
      +
      +Specify the path to the file that contains the email to be parsed. One of in_file +and handle must be specified. +
      +

      +
      handle
      +
      +
      +Specify the file handle or IO stream that contains the email to be parsed. +
      +

      +
      +
      attach_rfc822
      +
      +
      +By default, the parser will decode any embeded emails, and flatten out all the +parts. If you prefer to leave embeded emails unparsed, pass in 1 to this option +and the parser will treat it as an attachment. +
      +

      +

      +

      +

      parse - Parse an email

      +

      Instance method. Parses the email specified by either in_file or handle. Returns +the top level the GT::Mail::Parts manpage object. Any additional parameters passed in are +treated the same as if they were passed to the constuctor.

      +

      +

      +

      parse_head - Parse just the header of the email

      +

      Instance method. This method is exactly the same as parse except only the top +level header is parsed and it's part object returned. This is useful to keep +overhead down if you only need to know about the header of the email.

      +

      +

      +

      size - Get the size

      +

      Instance method. Returns the total size in bytes of the parsed unencoded email. This +method will return undef if no email has been parsed.

      +

      +

      +

      all_parts - Get all parts

      +

      Instance method. Returns all the parts in the parsed email. This is a flatened +list of the objects. Somewhat similar to what MIME::Tools does. All the parts +still contain their sub parts.

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Parse.pm,v 1.79 2004/10/23 02:16:39 brewt Exp $

      + + + + diff --git a/site/glist/templates/help/GT/Mail/Parts.html b/site/glist/templates/help/GT/Mail/Parts.html new file mode 100644 index 0000000..8b7d053 --- /dev/null +++ b/site/glist/templates/help/GT/Mail/Parts.html @@ -0,0 +1,570 @@ + + + +GT::Mail::Parts - Data storage class for MIME parts + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Mail::Parts - Data storage class for MIME parts

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::Mail;
      +
      +    my $mail = new GT::Mail;
      +
      +    my $top_part = $mail->parse('/path/to/email');
      +
      +    # Access the emails as an array
      +    my @to   = $top_part->split_field('to');
      +    my @from = $top_part->split_field('from');
      +
      +    # Access to the header fields
      +    my $mailer  = $top_part->get('X-Mailer');
      +    my $subject = $top_part->get('Subject');
      +
      +    # Access to this parts sub part
      +    if ($top_part->is_multipart) {
      +        my @parts = $top_parts->parts;
      +        for my $part (@parts) {
      +
      +            # Access parts of the header
      +            print "Filename: ", $part->recommended_filename, "\n";
      +            print "Part is multi-part\n" if $part->is_multipart;
      +
      +            # Get the body as a string
      +            my $body = $part->body_as_string;
      +        }
      +    }
      +
      +    # Change who it is to
      +    $top_part->set('to', 'scott@gossamer-threads.com');
      +
      +    # Remove the bcc line
      +    $top_part->delete('bcc');
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::Mail::Parts is a class to provide methods to change and +access a MIME messages. The object for this class is meant to +be istansiated from the GT::Mail manpage.

      +

      +

      +

      effective_type - Access the effective MIME type

      +
      +    my $type = $obj->effective_type;
      +
      +    if ($type eq 'application/octet-stream') {
      +        ...
      +    }
      +

      This method returns the effective MIME Type of this objects part.

      +

      +

      +

      get - Access header tags.

      +
      +    my $subj = $obj->get('Subject');
      +
      +    # or if there is more than one
      +    my @recv = $obj->get('Received');
      +

      Used to access any of the tags in the header of the MIME part. If the +tag requested is not present returns false. The first argument to this +method is the name of he tag you want to extract. This is case insensitive.

      +

      +

      +

      set - Set a header tag.

      +
      +    # Change who the email is to
      +    $obj->set('to', 'scott@gossamer-threads.com');
      +
      +    # Change the second Received tag
      +    $obj->set('Received', 'from unknown', 1);
      +

      Set any of the tags in the header. If the tag does not exist this will create +it. This method takes three arguments. The first is the name of the tag to +change or add, this is case insensitive. The second argument is the value for +the tag. The third zero based optional argument is the position. The position +will default to zero if it is not specified.

      +

      +

      +

      delete - Remove a header tag.

      +
      +    # Delete who the message is from
      +    $obj->delete('from');
      +

      This method deletes the tag specified by the first argument from this MIME +part.

      +

      +

      +

      size - Access the total size.

      +
      +    my $size = $obj->size;
      +

      This method returns the total size of this part. This includes the header and +the body.

      +

      +

      +

      preamble - Set or get the preamble.

      +
      +    # Retrieve the preamble
      +    my $pre = $obj->preamble;
      +
      +    # Set the preamble
      +    $obj->preamble('This is a multi-part message in MIME format.');
      +

      This is a set get method for the preamble. The preamble is the part after the +head but before the first MIME boundary. This method makes no since if this +is not a multi-part part.

      +

      +

      +

      epilogue - Set or get the epilogue.

      +
      +    # Retrieve the epilogue
      +    my $ep = $obj->epilogue;
      +
      +    # Set the epilogue
      +    $obj->epilogue('This is my cool epilogue');
      +

      This is a set get method for the epilogue. The epilogue is the part of the +MIME part after the MIME boundary and before the next head. This method makes +no since if this is not a multi-part part.

      +

      +

      +

      mime_type - Set or get the MIME type.

      +
      +    my ($type, $subtype) = split('/', $obj->mime_type);
      +

      This method returns the MIME type of this part. You can pass in an argument +to change the MIME type as well. So you could do

      +
      +    $obj->mime_type('text/plain');
      +

      This is probably not such a good idea unless you are constructing the email from +scratch.

      +

      +

      +

      is_multipart - See if you have a multi-part part.

      +
      +    if ($obj->is_multipart) {
      +        # do some multi-part stuff
      +    }
      +

      Returns true is this part is a multi-part MIME part.

      +

      +

      +

      parts - Access sub parts.

      +
      +    my @parts = $obj->parts;
      +

      Returns the parts object this part contains. Returns false if this part does +no have any sub parts. The parts objects that returns are from this same class. +Any parts that are milti-part should contain parts.

      +

      +

      +

      multipart_boundary - Set or get the multi-part boundary.

      +
      +    my $boundary = $obj->multipart_boundary;
      +

      This returns the multi-part boundary for this part. Setting this is never needed +and may be removed in the future. This method only makes since if you are working +with a multi-part pert.

      +

      +

      +

      header_as_string - Access the whole header.

      +
      +    my $head = $obj->header_as_string;
      +

      This method creates and returns the header for this part. The returned header should +be fully rfc822 compliant. Avoid calling this method more than once, as it will build +the header from an internal data structure each time.

      +

      +

      +

      split_field - Retrieve the emails split up into an array.

      +
      +    my @to  = $obj->split_field; # Defaults to 'to'
      +    my @bcc = $obj->split_field('bcc');
      +

      This is mostly a utility method. It takes an option argument as to the field you want +the email address from (default is to), it then splits the emails on '\s*,\s*' that is +not inside quotes. Returns an array of the split up string.

      +

      +

      +

      suggest_encoding - Get a suggestion for encoding.

      +
      +    my $encode = $obj->suggest_encoding;
      +

      Returns a suggested encoding for the body of this message. This is useful to decide +what encoding you should use for the body when building an email. This is used in +the GT::Mail::Parse manpage to decide how to encode the message body.

      +

      +

      +

      recommended_filename - Figure out the file name.

      +
      +    my $file = $obj->recommended_filename;
      +    if ($file) {
      +        ...
      +    }
      +

      This method tries to figure out the file name of this part. This does not make much +since if this part is not an attachment of some kind. Returns an empty string on +failure.

      +

      +

      +

      body_as_string - Get the body as a string.

      +

      This method returns the entire body of the MIME message as a string. You should not +use this method if the body could be large.

      +

      +

      +

      body_in - Find the body.

      +
      +    my $in = $obj->body_in;
      +    my $body;
      +
      +    if ($in eq 'MEMORY') {
      +        $body = $obj->body_data;
      +    }
      +    elsif ($in eq 'HANDLE') {
      +        $body = $obj->body_handle;
      +    }
      +    elsif ($in eq 'FILE') {
      +        $body = $obj->body_path;
      +    }
      +

      This method returns the location of the body. The location can be one of three things:

      +
      +
      MEMORY
      +
      +
      +The body is in a string. +
      +

      +
      HANDLE
      +
      +
      +The body is in an IO handle. +
      +

      +
      FILE
      +
      +
      +The body is in a file. +
      +

      +

      You would use this to decide what method to use to access the body. If the MIME message +was parsed into GT::Mail::Parts using the GT::Mail::Parser manpage the body will always be in +a FILE.

      +

      +

      +

      body_data - Get the in memory body.

      +

      This method returns the body if it is stored in memory. Returns undefined if the body is +not in memory.

      +

      +

      +

      body_handle - Get an IO handle to the body.

      +

      This method returns a handle to the body if the body is stored in a handle for this part. +Returns undefined if not.

      +

      +

      +

      body_path - Get the location of the file the body is in.

      +

      This method returns the file path to the file the body is located in if the body for this +part is stored in a file. Returns undefined if not.

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      $Revision: 1.77 $

      + + + + diff --git a/site/glist/templates/help/GT/Mail/Send.html b/site/glist/templates/help/GT/Mail/Send.html new file mode 100644 index 0000000..82411fa --- /dev/null +++ b/site/glist/templates/help/GT/Mail/Send.html @@ -0,0 +1,383 @@ + + + +GT::Mail::Send - Module to send emails + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Mail::Send - Module to send emails

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::Mail::Send;
      +    
      +    # $mail_object must be a GT::Mail object
      +    my $send = new GT::Mail::Send (
      +        mail  => $mail_object,
      +        host  => 'smtp.gossamer-threads.com',
      +        debug => 1
      +    );
      +
      +    $send->smtp or die $GT::Mail::Send::error;
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::Mail::Send is an object interface to sending email over either +SMTP or Sendmail. This module is used internally to GT::Mail.

      +

      +

      +

      new - Constructor method

      +

      Returns a new GT::Mail::Send object. You must specify either the smtp host +or a path to sendmail. This method is inherented from GT::Base. The arguments +can be in the form of a hash or hash ref.

      +
      +
      debug
      +
      +
      +Sets the debug level for this instance of GT::Mail::Send. +
      +

      +
      mail
      +
      +
      +Specify the mail object to use. This must be a GT::Mail object and must contain +an email, either passed in or parsed in. +
      +

      +
      host
      +
      +
      +Specify the host to use when sending by SMTP. +
      +

      +
      port
      +
      +
      +Specify the port to use when sending over SMTP. Defaults to 25. +
      +

      +
      path
      +
      +
      +Specify the path to sendmail when sending over sendmail. If the binary passed in +does not exist, undef will be returned and the error set in GT::Mail::Send::error. +
      +

      +
      flags
      +
      +
      +Specify the flags used to call sendmail. Defaults to -t -oi -oeq, see the Sendmail +guilde for sendmail for more info on the parameters to sendmail. +
      +

      +

      +

      +

      smtp

      +

      Class or instance method. Sends the passed in email over SMTP. If called as a class +method, the parameters passed in will be used to call new(). Returns true on error, +false otherwise.

      +

      +

      +

      sendmail

      +

      Class or instance method. Send the passed in email to sendmail using the specified +path and flags. If called as a class method all additional arguments are passed to the +new() method. Returns true on success and false otherwise.

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Send.pm,v 1.53 2004/08/23 20:07:44 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/Plugins.html b/site/glist/templates/help/GT/Plugins.html new file mode 100644 index 0000000..7d6c114 --- /dev/null +++ b/site/glist/templates/help/GT/Plugins.html @@ -0,0 +1,386 @@ + + + +GT::Plugins - a plugin interface for Gossamer Threads products. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Plugins - a plugin interface for Gossamer Threads products.

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::Plugins;
      +    $PLUGIN = GT::Plugins->new('/path/to/plugin/dir');
      +
      +    $PLUGIN->dispatch(hook_name => \&code_ref => @args);
      +    $PLUGIN->dispatch_method(hook_name => $self => method => @args);
      +

      Old style, now deprecated in favour of the object approach above:

      +
      +    use GT::Plugins;
      +
      +    GT::Plugins->dispatch('/path/to/plugin/dir', hook_name => \&code_ref => @args);
      +    GT::Plugins->dispatch_method('/path/to/plugin/dir', hook_name => $self => method => @args);
      +

      +

      +
      +

      DESCRIPTION

      +

      The plugin module supports two modes of use. The first mode involves creating +and using a GT::Plugins object upon which plugin dispatch methods may be called +to provide hooks. The second does not use the object, but instead uses class +methods with an extra argument of the plugin path preceding the other +->dispatch() arguments.

      +

      Of the two approaches, the object approach is recommended as it is a) faster, +and b) requires much less value duplication as the plugin directory needs to be +specified only once. The old, class-method-based plugin interface should be +considered deprecated, and all new code should attempt to use the object-based +system.

      +

      A dispatch with each of the two interfaces work as follows, with differences in +interfaces as noted:

      +
        +
      1. +Loads the plugin config file. The actual file access and evaluation will be +cached, but a small amount of extra overhead is required on each dispatch. +This only applies to the deprecated class-method dispatch interface - the +preferred object interface loads the configuration file only once. +

        +
      2. +Runs any 'PRE' hooks registered in the config file. When using ->dispatch(), +each hook is passed the @args arguments passed into ->dispatch. When using +->dispatch_method(), both the object ($self) and arguments (@args) are passed +to the hook. +

        Each plugin hook then has the ability to abort further plugins if desired by +calling $PLUGIN->action(STOP) (or GT::Plugins->action(STOP) for +the non-OO interface). STOP is exported by default from the GT::Plugins +module. Performing a STOP will skip both any further 'PRE' hooks and the +original function/method, and will use the hook's return value instead of the +real code's return value.

        +

        The current behaviour of 'PRE' hooks ignores the return value of any 'PRE' hook +that does not perform a STOP, however this behaviour may change to use the +return value as the arguments to the next PRE hook or actual code called. As +such, it is strongly recommended to return @_ from any 'PRE' hooks.

        +

        +
      3. +Assuming ->action(STOP) has not been called, the method +(->dispatch_method) or code reference (->dispatch) will be called, and its +return value stored. +

        +
      4. +Any registered 'POST' hooks registered in the config file will be run. When +using ->dispatch(), the list-context return value of the main code run (or, if +a 'PRE' hook called STOP, the return value of that 'PRE' hook) will be passed +in. When using ->dispatch_method(), the object is additionally passed in as +the first argument. +

        The list returned by the 'POST' hook will be used as arguments for any +subsequent 'POST' hooks and as the final result returned by the ->dispatch() or +->dispatch_method() call. There is one exception to this - for +->dispatch_method() 'POST' hooks, if the first argument of the return value is +the object, it will be removed; this is done to prevent a build-up of excess +objects at the beginning of the 'POST' hook arguments/return values due to +'POST' hooks simply returning @_ unaltered.

        +

        +
      5. +The return value of the final 'POST' hook, or, when no post hooks are +configured, of the actual code, is returned as the result of the ->dispatch() +call. +

      +

      +

      +
      +

      SEE ALSO

      +

      Also included as part of the plugin system are some modules for web based tools +to manage plugins:

      +

      the GT::Plugins::Manager manpage - Add, remove and edit plugin files.

      +

      the GT::Plugins::Wizard manpage - Create shell plugins.

      +

      the GT::Plugins::Installer manpage - Used in installing plugins.

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2005 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Plugins.pm,v 1.55 2005/04/01 00:16:51 brewt Exp $

      + + + + diff --git a/site/glist/templates/help/GT/Plugins/Author.html b/site/glist/templates/help/GT/Plugins/Author.html new file mode 100644 index 0000000..94370f3 --- /dev/null +++ b/site/glist/templates/help/GT/Plugins/Author.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/Plugins/Author.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/Plugins/Installer.html b/site/glist/templates/help/GT/Plugins/Installer.html new file mode 100644 index 0000000..f8352e3 --- /dev/null +++ b/site/glist/templates/help/GT/Plugins/Installer.html @@ -0,0 +1,397 @@ + + + +/tmp/glist/private/lib/GT/Plugins/Installer.pm + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Plugins::Installer

      +

      +

      +
      +

      SYNOPSIS

      +
      +    $mgr->install_hooks('PluginName', ['hook_name', 'PRE|POST', 'code']);
      +    $mgr->install_menu('PluginName', ['menu_name', 'menu_url', 'enabled']);
      +    $mgr->install_options('PluginName', ['option_key', 'option_val', 'instructions']);
      +

      +

      +
      +

      DESCRIPTION

      +

      The installer is an object that is passed into plugins during installation. +It provides methods to add hooks, menu options, admin options or copy files +into the users application.

      +

      +

      +

      install_hooks

      +

      install_hooks takes as arguments the plugin name and an array of:

      +
      +
      hook_name
      +
      +
      +The hook you want to override. +
      +

      +
      PRE/POST
      +
      +
      +Either the string PRE or POST depending on whether the hook should be run +before the main code, or after. +
      +

      +
      code
      +
      +
      +The name of the code to run. It should be Plugins::PACKAGE::YourPluginName::function. +Where PACKAGE is the name of the Gossamer Product the plugin is for. For example +Plugins::GMail::Wap::header +
      +

      +

      install_hooks returns 1 on success, undef on failure with the error +message in $GT::Plugins::error.

      +

      +

      +

      install_menu

      +

      install_menu takes as arguments the plugin name and an array of:

      +
      +
      menu_name
      +
      +
      +The name that will show up in the admin menu. +
      +

      +
      menu_url
      +
      +
      +The URL for the menu option. +
      +

      +
      enabled
      +
      +
      +Either true or false depending on whether the menu option should be shown. +
      +

      +

      install_menu returns 1 on success, undef on failure with the error +message in $GT::Plugins::error.

      +

      +

      +

      install_options

      +

      install_options takes as arguments the plugin name and an array of:

      +
      +
      option_key
      +
      +
      +This is the key, and is used when accessing the options hash. +
      +

      +
      option_value
      +
      +
      +This is the default value. +
      +

      +
      instructions
      +
      +
      +A string instruction users on what the plugin does. +
      +

      +

      install_options returns 1 on success, undef on failure with the error +message in $GT::Plugins::error.

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Installer.pm,v 1.13 2004/08/23 19:54:27 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/Plugins/Manager.html b/site/glist/templates/help/GT/Plugins/Manager.html new file mode 100644 index 0000000..9ce5f98 --- /dev/null +++ b/site/glist/templates/help/GT/Plugins/Manager.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/Plugins/Manager.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/Plugins/Wizard.html b/site/glist/templates/help/GT/Plugins/Wizard.html new file mode 100644 index 0000000..3a89fe9 --- /dev/null +++ b/site/glist/templates/help/GT/Plugins/Wizard.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/Plugins/Wizard.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/RDF.html b/site/glist/templates/help/GT/RDF.html new file mode 100644 index 0000000..bb60b7c --- /dev/null +++ b/site/glist/templates/help/GT/RDF.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/RDF.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL.html b/site/glist/templates/help/GT/SQL.html new file mode 100644 index 0000000..5e1f351 --- /dev/null +++ b/site/glist/templates/help/GT/SQL.html @@ -0,0 +1,582 @@ + + + +GT::SQL - A database independent perl interface + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::SQL - A database independent perl interface

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::SQL;
      +
      +    my $db      = GT::SQL->new('/path/to/def');
      +    my $table   = $db->table('Links');
      +    my $editor  = $db->editor('Links');
      +    my $creator = $db->creator('NewTable');
      +    my $html    = $db->html('Links', new CGI);
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::SQL is a perl database abstraction layer to relational databases, providing +a native Perl interface rather than a query-based interface.

      +

      A GT::SQL object provides the interface to the entire database by providing +objects that are able to perform the work needed.

      +

      +

      +

      Creating a new GT::SQL object

      +

      There are two ways to get a GT::SQL object. First, you can simply provide the +path to the def file directory where GT::SQL stores all it's information:

      +
      +    $db = GT::SQL->new('/path/to/def');
      +

      or you can pass in a hash or hash ref and specify options:

      +
      +    $db = GT::SQL->new(
      +        def_path => '/path/to/def',
      +        cache    => 1,
      +        debug    => 1,
      +        subclass => 1
      +    );
      +

      You must specify def_path. Setting cache => 1 will result in all table +and relation objects being cached, which provides a performance improvement in +any situation where the same table or relation is used again.

      +

      Specifying subclass => 0 or subclass => 1 will enable or disable +the ability to subclass any of the objects GT::SQL creates. The default +value is 1, and should not normally be changed.

      +

      GT::SQL has significant amounts of debugging output that can be enabled by +specifying a value of 1 to the debug option. Larger values can be +specified for more detailed debugging output, however a level of 1 is almost +always more than sufficient. The accepted values are as follows:

      +
      +
      Level 0
      +
      +
      +This is the default, no debugging information is printed to stderr. All errors +can be obtained in $GT::SQL::error. +
      +

      +
      Level 1
      +
      +
      +All queries will be displayed to stderr. This is the recommended value if +query debugging is desired. +
      +

      +
      Level 2
      +
      +
      +Same as level 1, but includes more detailed information. Also, when calling +query_stack you get a stack trace on what generated each query. Not +recommended except when working directly on GT::SQL. +
      +

      +
      Level 3
      +
      +
      +Very detailed debug logs including creation and destruction of objects. +query_stack generates a javascript page with query, stack trace, and data dump +of arguments, but can be extremely large. Not recommended except for debugging +GT::SQL internals. +
      +

      +

      Pass in a def path

      +
      +    $obj = GT::SQL->new('/path/to/def/directory');
      +

      This method of calling new is also supported, however has the drawback that +none of the above options can be provided.

      +

      +

      +

      Getting Connected

      +

      GT::SQL loads the database connection info from database.def which is located +in the defs directory.

      +

      To create this file, you call set_connect() as follows:

      +
      +    $obj->set_connect({
      +        driver     => 'mysql',
      +        host       => 'localhost',
      +        port       => 3243,
      +        database   => 'databasename',
      +        login      => 'username',
      +        password   => 'password',
      +        PREFIX     => 'prefix_'
      +    });
      +

      This will test the database information, and save it to the def file. All +future connections will automatically use this connection information.

      +

      Not all of the arguments in this hash are necessary; some have reasonable +defaults for the connection.

      +
      +
      driver
      +
      +
      +This needs to be the driver that is being used for the connection. The default +for this is mysql. Driver names are case-insensitive. Available drivers +are: +
      +
      +
      MySQL
      +
      +
      +Driver for MySQL databases. Requires that the DBD::mysql module be installed. +
      +

      +
      Pg
      +
      +
      +Driver for PostgreSQL databases. Requires that the DBD::Pg module be +installed. +
      +

      +
      MSSQL
      +
      +
      +Driver for MSSQL 7.0 and above. Requires that the DBD::ODBC module be +installed. +
      +

      +
      Oracle
      +
      +
      +Driver for Oracle 8 and above. Requires the DBD::Oracle module. +
      +

      +
      host
      +
      +
      +This will specify the host to connect to. The default, which is acceptable for +most installations, is localhost. +
      +

      +
      port
      +
      +
      +This is the port on which to connect to the SQL server. The default for this +is to allow the DBI driver to choose the default, which is almost always the +appropriate choice. +
      +

      +
      database
      +
      +
      +This is the database name to use on the SQL server. This is required to +connect. For MSSQL, this is the Data Source name. +
      +

      +
      PREFIX
      +
      +
      +This specifies a prefix to use for table names. See the Table Prefixes +section below for more information. +
      +

      +

      +

      +

      Supported Objects

      +

      The following objects can be obtained through a GT::SQL object:

      +
      +
      Table/Relation
      +
      +
      +To get a table or relation object for working with SQL tables, you should call: +
      +
      +
      +    my $table = $db->table('table_name');
      +
      +
      +

      or for a table join:

      +
      +
      +
      +    my $relation = $db->table('table_name', 'other_table');
      +
      +
      +

      See the GT::SQL::Table manpage for more information on how to use a table object.

      +
      +

      +
      Creator
      +
      +
      +To create new tables, you need to use a creator. You can get one by calling: +
      +
      +
      +    my $creator = $db->creator('new_table');
      +
      +
      +

      where new_table is the name of the table you wish to create. See +the GT::SQL::Creator manpage for more information on how to use a creator object.

      +
      +

      +
      Editor
      +
      +
      +To edit existing tables (i.e. add/drop/change columns, add/drop indexes, etc.) +you need an editor object: +
      +
      +
      +    my $editor = $db->editor('existing_table');
      +
      +
      +

      where existing_table is the name of the table you wish the modify. See +the GT::SQL::Editor manpage for more information on how to use an editor object.

      +
      +

      +
      HTML
      +
      +
      +To get an html object for generating forms and html output, you need to pass in +the table/relation object you want to work with, and a cgi object: +
      +
      +
      +    my $html = $db->html($table, $cgi);
      +
      +
      +

      The html object uses information found in CGI to set values, etc. See +the GT::SQL::Display::HTML manpage for more information on how to use a html object.

      +
      +

      +

      +

      +

      Table Prefixes

      +

      GT::SQL supports the concept of table prefixes. If you specify a prefix using +the accessor, it is saved in the database.def file and will be used in all +future calls to table(), editor() and creator().

      +

      To set a prefix:

      +
      +    $db->prefix("foo");
      +

      to get the current prefix:

      +
      +    my $prefix = $db->prefix;
      +

      What this will do is transparently prepend foo to the beginning of every +table name. This means anywhere you access the table bar, the actual table +stored on the SQL server will be foobar. Note that the prefix should not +be included when getting table/creator/editor/etc. objects - the prefix is +handled completely transparently to all public GT::SQL functionality.

      +

      +

      +

      Query Stack

      +

      To display a list of all raw SQL queries sent to the database you can use:

      +
      +    my @queries = $db->query_stack;
      +

      or to have them formatted try

      +
      +    print $db->query_stack_disp;
      +

      which will join them up, displayed nicely. This is also available as a class +method:

      +
      +    print GT::SQL->query_stack_disp;
      +

      +

      +
      +

      SEE ALSO

      +

      the GT::SQL::Table manpage

      +

      the GT::SQL::Editor manpage

      +

      the GT::SQL::Creator manpage

      +

      the GT::SQL::Types manpage

      +

      the GT::SQL::Admin manpage

      +

      the GT::SQL::Display::HTML manpage

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: SQL.pm,v 1.111 2005/04/14 20:22:37 alex Exp $

      + + + + diff --git a/site/glist/templates/help/GT/SQL/Admin.html b/site/glist/templates/help/GT/SQL/Admin.html new file mode 100644 index 0000000..098d6db --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Admin.html @@ -0,0 +1,411 @@ + + + +GT::SQL::Admin - instant admin for any sql table. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::SQL::Admin - instant admin for any sql table.

      +

      +

      +
      +

      SYNOPSIS

      +
      +    my $cgi   = new GT::CGI;
      +    my $db    = new GT::SQL '/path/to/def';
      +    my $admin = new GT::SQL::Admin;
      +    if ($admin->for_me($cgi)) {
      +        $admin->process ( db => $db, cgi => $cgi );
      +    }
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::SQL::Admin provides an easy way to build a table/relation +management application. It provides all the HTML and code to +easily:

      +
      +    1. Add records
      +    2. Delete records
      +    3. Modify records
      +    4. Search records
      +    5. Add columns
      +    6. Drop columns
      +    7. Alter table properties
      +    8. Import data
      +    9. Export data
      +

      all in about 6 lines of code.

      +

      +

      +

      Usage

      +

      To use GT::SQL::Admin you need to pass in an existing +the GT::SQL manpage object, and a the GT::CGI manpage object.

      +

      In it's simplest usage, you can simply call:

      +
      +    my $admin = new GT::SQL::Admin;
      +    $admin->process ( db => $db, cgi => $cgi );
      +

      and the admin module will figure out what was requested and display +the appropriate screen. There is a $admin->for_me method that will +look to see if the cgi object contains something for the admin +to do, returning 1 if yes, 0 otherwise. You would then do:

      +
      +    my $cgi = new GT::CGI;
      +    my $admin = new GT::SQL::Admin;
      +    if ($admin->for_me($cgi)) {
      +        $admin->process ( db => $db, cgi => $cgi );
      +    }
      +

      You can also call any of the methods individually. You can create an +add form like:

      +
      +    $admin->add_form;
      +

      and it will be printed to STDOUT.

      +

      To change the look of a page, you can pass in strings or code refs +to display any of the following items:

      +
      +    start_html
      +    header
      +    start_form
      +    end_form
      +    footer
      +    end_html
      +

      and the admin will use your html/code when displaying. You can also pass +in to process:

      +
      +        record       => 'MyObject'
      +

      and the admin will use that string when displaying titles like 'Add MyObject'. +If you don't specify, it will default to the name of the table.

      +

      +

      +

      Subclassing the admin

      +

      You can enhance the functionality of an admin quite easily. By default +GT::SQL::Admin expects to find a GT::SQL object, a GT::CGI object, and uses +internally a GT::SQL::Display::HTML object for any form/record html +generation.

      +

      Alternatively, you can subclass one or more of the above and use your +own libraries. For instance, if you wanted to expand the form generation, +you could subclass the GT::SQL::Display::HTML object and override the display() +and form() method with your own.

      +

      The admin will pass in a 'mode' to both display and form that will tell +you what it is using the form for. This can be one of:

      +
      +    search_form
      +    search_results
      +    add_form
      +    add_success
      +    delete_search_form
      +    delete_search_results
      +    download_file
      +    modify_search_form
      +    modify_search_results
      +    modify_form
      +    modify_success
      +    modify_multi_search_results
      +    modify_multi_results_norec
      +    modify_multi_result_changed
      +    modify_multi_results_err
      +

      There are also several options that can be passed in. See the +the GT::SQL::Display::HTML manpage module for more information. + +

      +

      Also be sure to read about subclassing in the GT::SQL manpage. + +

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/ + +

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Admin.pm,v 1.146 2005/03/15 00:35:29 brewt Exp $ + +

      + + + + diff --git a/site/glist/templates/help/GT/SQL/Base.html b/site/glist/templates/help/GT/SQL/Base.html new file mode 100644 index 0000000..43ea8d6 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Base.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Base.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Condition.html b/site/glist/templates/help/GT/SQL/Condition.html new file mode 100644 index 0000000..68479d5 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Condition.html @@ -0,0 +1,390 @@ + + + +GT::SQL::Condition - Creates complex where clauses + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::SQL::Condition - Creates complex where clauses

      +

      +

      +
      +

      SYNOPSYS

      +
      +    my $cond = GT::SQL::Condition->new(Column => LIKE => 'foo%');
      +    print $cond->sql;
      +
      +    my $cond = GT::SQL::Condition->new(
      +        Column  => LIKE => 'foo%',
      +        Column2 => '<'  => 'abc'
      +    );
      +    $cond->bool('OR');
      +    print $cond->sql;
      +

      +

      +
      +

      DESCRIPTION

      +

      The condition module is useful for generating complex SQL WHERE clauses. At +it's simplest, a condition is composed of three parts: column, condition and +value.

      +

      Here are some examples.

      +

      To find all users with a first name that starts with Alex use:

      +
      +    my $cond = GT::SQL::Condition->new(FirstName => LIKE => 'Alex%');
      +

      To find users with first name like alex, and last name like krohn use:

      +
      +    my $cond = GT::SQL::Condition->new(
      +        FirstName => LIKE => 'Alex%',
      +        LastName  => LIKE => 'Krohn%'
      +    );
      +

      To find users with first name like alex or last name like krohn use:

      +
      +    my $cond = GT::SQL::Condition->new(
      +        FirstName => LIKE => 'Alex%',
      +        LastName  => LIKE => 'Krohn%'
      +    );
      +    $cond->bool('OR');
      +

      You may also specify this as:

      +
      +    my $cond = GT::SQL::Condition->new(
      +        FirstName => LIKE => 'Alex%',
      +        LastName  => LIKE => 'Krohn%',
      +        'OR'
      +    );
      +

      Now say we wanted something a bit more complex that would normally involve +setting parentheses. We want to find users who have either first name like alex +or last name like krohn, and whose employer is Gossamer Threads. We could use:

      +
      +    my $cond1 = GT::SQL::Condition->new(
      +        'FirstName', 'LIKE', 'Alex%',
      +        'LastName', 'LIKE', 'Krohn%'
      +    );
      +    $cond1->bool('or');
      +    my $cond2 = GT::SQL::Condition->new(
      +        $cond1,
      +        Employer => '=' => 'Gossamer Threads'
      +    );
      +

      By default, all values are quoted, so you don't need to bother using any quote +function. If you don't want something quoted (say you want to use a function +for example), then you pass in a reference.

      +

      For example, to find users who have a last name that sounds like 'krohn', you +could use your SQL engines SOUNDEX function:

      +
      +    my $cond = GT::SQL::Condition->new(LastName => '=' => \"SOUNDEX('krohn')");
      +

      and the right side wouldn't be quoted.

      +

      You can also use a condition object to specify a list of multiple values, which +will become the SQL 'IN' operator. For example, to match anyone with a first +name of Alex, Scott or Jason, you can do:

      +
      +    my $cond = GT::SQL::Condition->new(FirstName => IN => ['Alex', 'Scott', 'Jason']);
      +

      which will turn into:

      +
      +    FirstName IN ('Alex', 'Scott', 'Jason')
      +

      Note that when using multiple values, you can use '=' instead of 'IN'. Empty +lists will be treated as an impossible condition (1 = 0). This is primarily +useful for list handling list of id numbers.

      +

      To match NULL values, you can use undef for the value passed to the add() +method. If specifying '=' as the operator, it will automatically be changed to +'IS':

      +
      +    $cond->add(MiddleName => '=' => undef);
      +

      becomes:

      +
      +    MiddleName IS NULL
      +

      To negate your queries you can use the not function.

      +
      +    my $cond = GT::SQL::Condition->new(a => '=' => 5);
      +    $cond->not;
      +

      would translate into NOT (a = '5'). You can also do this all on one line like:

      +
      +    print GT::SQL::Condition->new(a => '=' => '5')->not->sql;
      +

      This returns the sql right away.

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Condition.pm,v 1.44 2004/10/12 17:54:30 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/SQL/Creator.html b/site/glist/templates/help/GT/SQL/Creator.html new file mode 100644 index 0000000..25f53e0 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Creator.html @@ -0,0 +1,553 @@ + + + +GT::SQL::Creator - an object to create SQL tables. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::SQL::Creator - an object to create SQL tables.

      +

      +

      +
      +

      SYNOPSIS

      +
      +    my $creator = $DB->creator('Newtable');
      +    $creator->cols(
      +        col1 => {
      +            pos => 1
      +            type => 'CHAR',
      +            size => 50
      +        },
      +        col2 => {
      +            pos => 2,
      +            type => 'INT',
      +            not_null => 1
      +        }
      +    );
      +    $creator->pk('col2');
      +    $creator->ai('col2');
      +    $creator->create or die "Unable to create: $GT::SQL::error";
      +

      +

      +
      +

      DESCRIPTION

      +

      A creator object is used to build new SQL tables.

      +

      To get a new creator object, you need to call creator() from an existing +GT::SQL object.

      +

      The object that is returned has methods to set up your table. You will need to +call this method for each table you want to create.

      +
      +    $creator = $obj->creator($table);
      +

      You must pass in the name of the table you want to create. This means if you +have a table named MyTable you must call ->creator with 'MyTable' +as the argument.

      +
      +    $creator = $obj->creator('MyTable');
      +

      From this point you can call create methods on your creator object to define +and create your table.

      +

      +

      +

      cols

      +

      cols is used to define the columns that will be in the new table by setting +properties such as the type, whether it allows null values, unsigned etc.

      +

      For detailed information on the types and options accepted, please see +the GT::SQL::Types manpage. The following describes the options accepted that do not +directly affect the underlying database:

      +
      +
      values
      +
      +
      +This specifies the values for the ENUM column type. If you are using an +ENUM this must be set. The value for this should be an array reference of +the possible values for the ENUM column. The values in the array that is +passed in will be quoted by DBI's quote method. +
      +

      +
      regex
      +
      +
      +This is a regex that the value must pass before being inserted +into the database. +
      +

      +
      form_display
      +
      +
      +This is a ``pretty name'' that will be used by the HTML module +for creating attractive forms automatically. +
      +

      +
      form_size
      +
      +
      +This is the form field length to be used by the HTML module. +
      +

      +
      form_type
      +
      +
      +This is the type of form to use by the HTML module: select, checkbox +radio, text, textarea or hidden. +
      +

      +
      form_names
      +
      +
      +This is for multi select or checkboxes and is an array ref of names +that get displayed. +
      +

      +
      form_values
      +
      +
      +This is for multi select or checkboxes and is an array ref of the +actual values that will be stored in the database. +
      +

      +
      time_check
      +
      +
      +This is only useful for TIMESTAMP fields. If set to 1, the module +will not allow you to update a record which has an older timestamp +then what is in the database. This is very helpful for protecting +against multiple updates. +
      +

      +
      weight
      +
      +
      +By giving an item a weight, GT::SQL will maintain a search index +table, and use that search index table when called using query. +This is only useful for indexing large text fields and should not +be used normally. The higher the weight, the more influence that +column will have on the result. So if a Title was set to weight +3 and a Description to weight 1, then when doing a search, a match +in the title would make the result appear before a match in the +description. +
      +

      +

      So an example would look like:

      +
      +    $creator->cols(
      +        $col1 => {
      +            type     => 'ENUM',
      +            values   => ['val1', 'val2' ... ],
      +            not_null => 1
      +        },
      +        $col2 => {
      +            ...
      +        }
      +    );
      +

      Sets the relations columns as specified via method +parameters. The only required key for the has is type. +However some column types require other values be set +such as ENUM requires you specify the values.

      +

      +

      +

      pk

      +

      pk lets you specify the primary keys for the current table. +This method can be called with an array of primary key columns +in which case all the specified column names in the array will +make up the primary keys. If you call it with a single scalar +value this is assumed to be the primary key for the table.

      +
      +    $creator->pk($field1, $field2, ...);
      +

      +

      +

      ai

      +

      This specifies the auto increment column for the current table. +There can be only one auto increment column per table, it must +be a numeric type, it must be not null and it must be the +primary key. This limitation is checked when you call create. +If it is not a numeric column type you will get a fatal error +when you call create. If any of the other limitations fail +the creator class will correct.

      +

      +

      +

      index

      +

      index allows you to specify the name and the columns for you +table indexes.

      +

      There are two ways to call this method.

      +

      You can set up all your indexes at once by calling it with +hash reference like this:

      +
      +    $creator->index({
      +        $index1 => [field1, field2],
      +        $index2 => [field3, field4]
      +    });
      +

      The keys to this hash reference are the index names and +the values are an array reference containing the columns +that are part of the named index. The order for these +columns are maintained during the create.

      +

      You can also pass in one index at a time like this;

      +
      +    $creator->index($index_name, $col1, ..., $coln);
      +

      The first argument is the name of the index and all the +rest are treated as columns that are part of this index. +Again the order of the columns are maintained.

      +

      +

      +

      unique

      +

      The unique method allows you to specify the unique +indexes for the current table. This method takes the +same arguments as the index method.

      +

      +

      +

      fk

      +

      fk allows you to specify foreign key relations for your +tables. You CAN NOT specify foreign keys for tables that +have not been created yet. There are two ways to pass in +arguments to fk. The first way is passing in a hash reference.

      +
      +    $creator->fk({
      +        $FOREIGN_TABLE_NAME =>
      +        {
      +            $LOCAL_TABLE_COL_1 => $FOREIGN_TABLE_COL_1,
      +            ...
      +            $LOCAL_TABLE_COL_n => $FOREIGN_TABLE_COL_n
      +        }
      +    });
      +

      The keys to the hash are the names of the tables you are relating to. +The values are a hash reference that contain the name of the current +tables columns as the keys and the name of the foreign tables columns +that we are relating to as the values.

      +

      You cannot relate fields to your self. You also need to be careful +not to create circular references. This is checked when you call this +method. If there is a circular reference detected you will receive a +fatal error.

      +

      Foreign keys currently effect selects only.

      +

      +

      +

      search_driver

      +

      This affects how the weighted records are indexed. By default the +system will attempt to use best driver for the DBMS. However, if +you'd like to force the indexing system to an alternative type, such +as for MYSQL you can use this.

      +

      * note: though the MYSQL driver is faster, the internal indexing system +has better support for phrase searching and keyword searching.

      +

      To set the driver, call search_driver with the appropriate driver +name. The following example will force the system into using the +internally implemented indexing scheme.

      +
      +    $creator->search_driver('INTERNAL');
      +

      Currently, the only other valid option is ``MYSQL''.

      +

      -note-

      +

      The MYSQL driver occasionally behaves oddly with a small number of +records. In that case, set the search scheme to ``INTERNAL''.

      +

      +

      +

      create

      +

      This is the method you call to create your table after you have specified +all your table definitions. Several checks are made when this method is +called to ensure the table is created correctly.

      +

      One of the things that is done is checking to see that the table you are +trying to create does not exist. If the table does exist create will +return undefined and set the error in $GT::SQL::error.

      +

      You can specify to have create drop the table by passing in ``force''.

      +
      +    $creator->create('force');
      +

      -or-

      +
      +    $creator->create;
      +

      create returns true on success and undef on failure.

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Creator.pm,v 1.74 2004/09/22 02:43:29 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/SQL/Display/HTML.html b/site/glist/templates/help/GT/SQL/Display/HTML.html new file mode 100644 index 0000000..38693a5 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Display/HTML.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Display/HTML.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Display/HTML/Relation.html b/site/glist/templates/help/GT/SQL/Display/HTML/Relation.html new file mode 100644 index 0000000..990389f --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Display/HTML/Relation.html @@ -0,0 +1,289 @@ + + + +/tmp/glist/private/lib/GT/SQL/Display/HTML/Relation.pm + + + + + + + + + +

      + + + +

      # Options for display forms/views: +# hide_timestamp => 1 # Do not display timestamp fields. +# search_opts => 1 # Add search options boxes. +# multiple => 1 # Prepend $multiple- to column names. +# defaults => 1 # Use .def defaults. +# values => {} # hash ref of values to use (overrides input) +# table => 'string' # table properties, defaults to 0 border. +# tr => 'string' # table row properties, defaults to none. +# td => 'string' # table cell properties, defaults to just aligns. +# extra_table => 0 # disable wrap form in extra table for looks. +# col_font => 'string' # font to use for columns, defaults to $FONT. +# val_font => 'string' # font to use for values, defaults to $FONT. +# hide => [] # display fields as hidden tags. +# view => [] # display fields as html with hidden tags as well. +# skip => [] # don't display array of column names. + +

      + + + + diff --git a/site/glist/templates/help/GT/SQL/Display/HTML/Table.html b/site/glist/templates/help/GT/SQL/Display/HTML/Table.html new file mode 100644 index 0000000..ac61c9a --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Display/HTML/Table.html @@ -0,0 +1,287 @@ + + + +/tmp/glist/private/lib/GT/SQL/Display/HTML/Table.pm + + + + + + + + + +

      + + + +

      # Options for display forms/views: +# hide_timestamp => 1 # Do not display timestamp fields. +# search_opts => 1 # Add search options boxes. +# multiple => 1 # Prepend $multiple- to column names. +# defaults => 1 # Use .def defaults. +# values => {} # hash ref of values to use (overrides input) +# table => 'string' # table properties, defaults to 0 border. +# tr => 'string' # table row properties, defaults to none. +# td => 'string' # table cell properties, defaults to just aligns. +# extra_table => 0 # disable wrap form in extra table for looks. +# col_font => 'string' # font to use for columns, defaults to $FONT. +# val_font => 'string' # font to use for values, defaults to $FONT. +# hide => [] # display fields as hidden tags. +# view => [] # display fields as html with hidden tags as well. +# skip => [] # don't display array of column names.

      + + + + diff --git a/site/glist/templates/help/GT/SQL/Driver.html b/site/glist/templates/help/GT/SQL/Driver.html new file mode 100644 index 0000000..6dc197b --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Driver.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Driver.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Driver/MSSQL.html b/site/glist/templates/help/GT/SQL/Driver/MSSQL.html new file mode 100644 index 0000000..4ef280e --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Driver/MSSQL.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Driver/MSSQL.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Driver/MYSQL.html b/site/glist/templates/help/GT/SQL/Driver/MYSQL.html new file mode 100644 index 0000000..9fa809f --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Driver/MYSQL.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Driver/MYSQL.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Driver/ORACLE.html b/site/glist/templates/help/GT/SQL/Driver/ORACLE.html new file mode 100644 index 0000000..151c6b4 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Driver/ORACLE.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Driver/ORACLE.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Driver/PG.html b/site/glist/templates/help/GT/SQL/Driver/PG.html new file mode 100644 index 0000000..2220940 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Driver/PG.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Driver/PG.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Driver/Types.html b/site/glist/templates/help/GT/SQL/Driver/Types.html new file mode 100644 index 0000000..f06e754 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Driver/Types.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Driver/Types.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Driver/debug.html b/site/glist/templates/help/GT/SQL/Driver/debug.html new file mode 100644 index 0000000..a0e70bb --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Driver/debug.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Driver/debug.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Driver/sth.html b/site/glist/templates/help/GT/SQL/Driver/sth.html new file mode 100644 index 0000000..7969f74 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Driver/sth.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Driver/sth.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Editor.html b/site/glist/templates/help/GT/SQL/Editor.html new file mode 100644 index 0000000..d30996c --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Editor.html @@ -0,0 +1,494 @@ + + + +GT::SQL::Editor - an interface to modify an SQL table. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::SQL::Editor - an interface to modify an SQL table.

      +

      +

      +
      +

      SYNOPSIS

      +
      +    my $editor = $DB->editor('Table');
      +    $editor->add_col(Foo => { size => 20, type => 'int' });
      +    $editor->export_data('/tmp/foo.txt');
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::SQL::Editor is an easy way to do a lot of table maintenance +functions like:

      +

      * Adding columns +* Dropping columns +* Changing columns +* Altering keys +* Importing data +* Dropping data

      +

      To get an editor object, you simply call editor from a +GT::SQL object, and specify the tablename you want to edit:

      +
      +    $editor = $db->editor('TableName');
      +

      Note: You can not use Editor with relations, only tables.

      +

      +

      +

      add_col

      +

      This method allows you to add a column to the current table. +All attributes for the column are passed in a single hash.

      +
      +    $editor->add_col($col_name, 
      +                        { 
      +                            size => 20, 
      +                            type => 'int',
      +                            view_size => 20, 
      +                            nice_name => "my col", 
      +                            regex => 'myregex' 
      +                        }
      +                    );
      +

      The same rules apply to this method that apply when you +define a column for creating a table. You must specify the +type.

      +

      +

      +

      drop_col

      +

      This method drops a column from the current table. Checks +are made to ensure the column is not linked to by a foreign +key relation.

      +
      +    $editor->drop_col($col_name);
      +

      -or-

      +
      +    $editor->drop_col($col_name, "remove");
      +

      If you just specify the column name drop_col will check if +the column is referenced in a foreign key relation. If it +is drop_col will return undef and set the error message in +$GT::SQL::error. If it is not the column will be dropped.

      +

      If you specify ``remove'' drop_col will remove all foreign +key relations that point to the specified column.

      +

      If the specified column is itself a foreign key relation, the relation will be +dropped.

      +

      +

      +

      alter_col

      +

      This allows you to make changes to a columns type, null status, +etc..

      +
      +    $editor->alter_col($column_name,
      +                                { 
      +                                    size => 20, 
      +                                    type => 'int' 
      +                                });
      +

      The first argument is the column name the second is the definitions. +The column definitions are exactly the same as the column +definitions from the create. The type must be specified.

      +

      You can not add attributes to the column in this way. +You must specify the original definitions along with the +changes you need to make.

      +

      +

      +

      add_unique

      +

      This allows you to add a unique index to the current table. +If the name of the unique index is the same as another +index you add_unique will return undef and set the error +in $GT::SQL::error.

      +
      +    $editor->add_unique($index_name => [ $field1, $field2 .. ]);
      +

      The name of the new index is the first argument. The second argument +is an array reference containing the columns that will be indexed. +The order of the columns are maintained for the unique index. +If you specify an index that has data in it that is not unique +(yes we do a select on the database) add_unique will return +an error and set the error in $GT::SQL::error.

      +

      +

      +

      drop_unique

      +

      This method allows you to drop a unique index for the current +table. If the unique index does not exist drop_unique will +return undef and set the error in $GT::SQL::error. drop_unique +will also check to make sure dropping the unique index will not +cause problems for the database structure. If dropping the unique +index will cause a problem drop_unique will return undef and set +the error in $GT::SQL::error.

      +
      +    $editor->drop_unique($index_name);
      +

      $index_name should be the name of the unique index to drop.

      +

      +

      +

      add_index

      +

      This takes the same arguments as add_unique and return the same thing. +The only difference is add_index has no reason to check the content of +the current table because indexes are not unique. unique indexes are :)

      +
      +    $editor->add_index($index_name => [ $field1, $field2 .. ]);
      +

      +

      +

      drop_index

      +

      This method drops the specified index from the current table. +drop_index will check to make sure no problems are caused from +dropping the index. If there are drop_index will return undef +and set the error in $GT::SQL::error.

      +
      +    $editor->drop_index($index_name);
      +

      $index_name should be the name of the index to drop.

      +

      +

      +

      add_pk

      +

      This method allows you to add a primary key to the current +database.

      +
      +    $editor->add_pk($field1, $field2, ...);
      +

      If there is already a primary key in the database add_pk +will drop the key and add the this new one. The table +will be check to make sure this change does not create problems +for the table. I problem is auto increment not being the primary +key anymore. If there is a problem this function returns undef +and stores the error in $GT::SQL::error.

      +

      +

      +

      drop_pk

      +

      This method drops the current primary key. If there is no primary +key to drop it returns undef and sets the error in $GT::SQL::error.

      +
      +    $editor->drop_pk;
      +

      If dropping the primary key will cause problems for the database +this method will return undef and set the error in $GT::SQL::error.

      +

      +

      +

      add_fk

      +

      This method allows you to add foreign key relations to the current +table.

      +
      +    $editor->add_fk($RELATION_NAME, { $SOURCE_FIELD_1 => $TARGET_FIELD });
      +

      You can not link your foreign key to tables that do not exist. Also the +columns types and lengths for the two columns must be the same. +Circularity is not allowed either. That is a set of foreign keys can not +end up pointing back at the same table they started at. All of these things +are checked when this is added. If anything does not match this method returns +undef and sets the error in $GT::SQL::error.

      +

      +

      +

      drop_fk

      +

      This method drops the specified foreign key relation.

      +
      +    $editor->drop_fk($table);
      +

      $table should be the name of the foreign table the foreign +key points to.

      +

      +

      +

      drop_table

      +

      This method drops the current table. If there are any foreign keys +pointing to this table this method will fail and return undef. The error +will be set in $GT::SQL::error.

      +
      +    $editor->drop_table;
      +

      -or-

      +
      +    $editor->drop_table("remove");
      +

      If the first argument to this method is remove it will remove all +the foreign key relations that point to this table.

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Editor.pm,v 1.76 2005/04/27 22:53:24 brewt Exp $

      + + + + diff --git a/site/glist/templates/help/GT/SQL/File.html b/site/glist/templates/help/GT/SQL/File.html new file mode 100644 index 0000000..143d4c7 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/File.html @@ -0,0 +1,404 @@ + + + +GT::SQL::File - adds file upload and download abilities to GT::SQL + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::SQL::File - adds file upload and download abilities to GT::SQL

      +

      GT::SQL::File::Fh - basic file object

      +

      +

      +
      +

      DESCRIPTION

      +

      GT::SQL::File is not created directly by the user. This module is an +internal module for GT::SQL to provide the abilty to upload/download +files into a database column (or so it seems).

      +

      GT::SQL::File::Fh is often accessed by the user as well as created +by the user whenever the user wants to store a file in the database.

      +

      +

      +

      Creating a new FILE Column

      +

      When a new table is created or a column is converted into 'FILE' +type, two things are created. First a column of type text which will +save the name of the file that is being stored. Secondly, a +piggy-back table will be greated under the name +``parent_table_name_File''. This new table will store the location of +the uploaded/stored file and various associated file attributes.

      +

      To create a new file table, include a column something like the +following.

      +
      +    File_Col_Name => {
      +
      +                # common parameters
      +                       pos  => 2, 
      +                       type => 'FILE',
      +
      +                # location of the directory where
      +                # all the files should be saved
      +                       file_save_in => '/tmp',
      +
      +                # the method all the files are saved
      +                # 'hashed', or 'simple'
      +                #
      +                # Defaults to hashed, and stores files in:
      +                #   file_save_in/hashed_letter/ID
      +                # Simple stores files in:
      +                #   file_save_in/ID_OwnName.OwnExt
      +                       file_save_scheme => 'hashed',
      +                     } ...
      +

      +

      +

      Inserting into the Column

      +

      Once you have the table created, to insert:

      +
      +    # Include all the modules
      +    use GT::SQL;
      +    use GT::SQL::File;
      +    
      +    # First create a file object pointing to the file
      +    $f = GT::SQL::File->open('/path/to/file.txt');
      +    
      +    # Then create a table object
      +    $DB = GT::SQL->new('path/to/defs');
      +    $tbl = $DB->table();
      +    
      +    # Create the record
      +    # the file field can also be GT::CGI::Fh type
      +    $rec = {
      +        File_Column => $f,
      +        # ... and all the other columns
      +    };
      +

      # optionally, if you know the path to the file, you can provide +# a scalar ref of the path and the module will autoload +# the values +# simple scalar values will be dropped + $rec = { + File_Column => \``/path/to/file.txt'' + # ... and all the other columns + }; +

      +
      +
      +    # Then to store the file
      +    $id = $tbl->add( $rec );
      +

      +

      +

      Retreiving from Column

      +

      When a file has been stored. A standard select will only return +the name of the file.

      +

      To get a filehandle, taking the previous example, if we know the +unique id, you can do the following.

      +
      +    $fh = $tbl->file_info( 'File_Column', $id );
      +

      You can use this file handle just like any other, however hidden +behind are special functions that can be used as follows:

      +
      +    print "Content-type: ", $fh->File_MimeType(), "\n\n";
      +    print <$fh>;
      +

      The following is a partial list of special functions you may access.

      +
      + 
      +    Method             Returns
      +    ------             -------
      +    File_Name          the basic filename
      +    File_Directory     path to the file
      +    File_MimeType      mimetype of the file
      +    File_Size          site of the file
      +    File_RelativePath  the permuted file and directory without root
      +    File_URL           if possible, the URL to the requested file
      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: File.pm,v 1.60 2004/08/28 03:53:43 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/SQL/Monitor.html b/site/glist/templates/help/GT/SQL/Monitor.html new file mode 100644 index 0000000..ec67d65 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Monitor.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Monitor.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Relation.html b/site/glist/templates/help/GT/SQL/Relation.html new file mode 100644 index 0000000..92dae50 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Relation.html @@ -0,0 +1,601 @@ + + + +GT::SQL::Relation - manage multiple table joins + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::SQL::Relation - manage multiple table joins

      +

      +

      +
      +

      SYNOPSIS

      +
      +    my $relation = $DB->table('Company', 'Employees');
      +    my $sth = $relation->select( {
      +                    Company.Name => 'Gossamer Threads',
      +                    Employees.Name => 'Alex Krohn'
      +                }, ['Employees.Salary', 'Company.City'] );
      +    my ($salary, $city) = $sth->fetchrow_array;
      +    print "Alex works in $city and earns $salary!\n";
      +

      +

      +
      +

      DESCRIPTION

      +

      This module aims at emulating a set of tables that are related to each other +via the use of foreign keys just as if it was one big table.

      +

      The module interface should be as compatible as possible with GT::SQL::Table, +thus you should be familiar with GT::SQL::Table before even reading this.

      +

      This documentation explains the differences between GT::SQL::Relation and +GT::SQL::Table and how the module internally works as well.

      +

      +

      +

      How it works

      +

      GT::SQL supports the concept of foreign keys (also known as external +references). Basically, two tables that are linked together using external +references can look like that:

      +
      +    .-------------.      .---------.
      +    | EMPLOYEE    |      | COMPANY |
      +    `-------------'      `---------'
      +    |  ID         |   .--->ID      |
      +    |  COMPANY_ID ----'  | NAME    |
      +    |  NAME       |      `---------'
      +    |  SALARY     |
      +    `-------------'
      +

      In this example, the COMPANY_ID attribute relates the fact that a an EMPLOYEE +belongs to such or such COMPANY.

      +

      Utilizing a Relation object can make these tables look like that:

      +
      +    .----------------------.
      +    | EMPLOYEE-COMPANY     |
      +    `----------------------'
      +    |  EMPLOYEE.ID         |
      +    |  EMPLOYEE.COMPANY_ID |
      +    |  EMPLOYEE.NAME       |
      +    |  EMPLOYEE.SALARY     |
      +    |  COMPANY.NAME        |
      +    `----------------------'
      +

      The first thing that can be seen from there is that COMPANY.ID has disappeared +from this ``Virtual'' table.

      +

      Indeed, as for a given ``joined'' record this value must be the same in both +tables, representing the values twice would have been a useless source of +confusion.

      +

      +

      +

      SELECT statements

      +

      Selecting from a Relation object is pretty simple using the GT::SQL module. As +the interface is (almost) the same as the GT::SQL::Table manpage, the GT::SQL wrapper +returns Table or Relation objects depending on the arguments that are passed to +table.

      +
      +    # This gives me a GT::SQL::Table object for
      +    # the EMPLOYEE table.
      +    my $emp = $sql->table('EMPLOYEE');
      +
      +    # This gives me a GT::SQL::Relation object for
      +    # the relation EMPLOYEE-COMPANY tables
      +    my $emp_cmp = $sql->table('EMPLOYEE','COMPANY');
      +

      From there, performing a select is pretty simple:

      +
      +    # select all the people from a real cool company
      +    my $sth = $emp_cmp->select( { COMPANY.NAME => "Gossamer Threads" } )
      +

      Internally, the generated SQL query would look like:

      +
      +    SELECT EMPLOYEE.ID, EMPLOYEE.COMPANY_ID, EMPLOYEE.NAME
      +       EMPLOYEE.SALARY, COMPANY.NAME
      +    FROM   EMPLOYEE, COMPANY
      +    WHERE  COMPANY.NAME = 'Gossamer Threads' AND
      +           EMPLOYEE.COMPANY_ID = COMPANY.ID
      +

      Note that the join condition is computed and automatically appended at the end +of the query, so you do not have to worry about this.

      +

      +

      +

      SELECT options

      +

      The select options for relation are similar to that of table, you have +select_options() which will be set for the next query done. Example:

      +
      +    $relation->select_options("LIMIT 10");
      +

      This would append 'LIMIT 10' to your next select query. Another useful thing +is join_on(). join_on() allows you to specify the FK relation for the nextr +select. This overrides what is in the def files. It is useful for allowing you +to have one table which will be join differently depending on what you are +doing. The argument to this are the same as to fk(). +Example:

      +
      +    $relation->join_on( remote_table => { local_column => remote_column } );
      +

      The FK relation will be changed to this the next time you call select() but +then it will be cleared.

      +

      +

      +

      Listing the relation columns

      +

      * As previously said, the cols() method when invoked on a GT::SQL::Relation +object does not return all the columns, removing the duplicate external +references. So, how does it decides which column to keep and which one to +return?

      +

      In the EMPLOYEE-COMPANY example we have the constraint +EMPLOYEE.COMPANY_ID => COMPANY.ID and it keeps COMPANY_ID, i.e. the foreign key +instead of the key itself.

      +

      +

      +

      Relation primary key

      +

      * The pk() method has to return the table primary key. The property of a primary +key is that it is a non-null unique record identifier. When pk() is invoked on +a Relation object, this base definition is applied to construct the object +primary key.

      +

      To find a unique set of fields that makes a good primary key for a Relation +object, the following, simple algorithm is used:

      +
      +    .                                                        .
      +    . for each table                                         .
      +    .   if the table is not referenced by another table that .
      +    .   is in the current relation                           .
      +    .     do                                                 .
      +    .       append the current table's primary key fields to .
      +    .       the Relation primary key fields                  .
      +    .     end-do                                             .
      +    .   end-if                                               .
      +    . end-for                                                .
      +    .                                                        .
      +

      This algorithm selects all the tables that represent the ``many'' in one-to-many +relations, and for all these tables add a list of fields which ensure a record +uniqueness.

      +

      +

      +

      Foreign keys management

      +

      * When invoked on a GT::SQL::Table object, the fk() method returns a hash which +has the following general structure:

      +
      +    {
      +      target_table_1 => {
      +                          source_col_1 => target_col_1,
      +                          source_col_2 => target_col_2
      +                        },
      +      target_table_2 => {
      +                          source_col_1 => target_col_1
      +            }
      +    }
      +

      The GT::SQL::Relation module returns a hash which has the same structure. The +only difference is that it does not returns the external references which are +managed internally.

      +

      This is done for two reasons: As one field is removed from a Relation table, it +would not have been very logical to return a structure that point to +non-existent fields.

      +

      Moreover, these internal references from the ``Relation'' point of view have +nothing to do with the external world and thus should not be shown.

      +

      (i.e. EMPLOYEE.COMPANY_ID |===> COMPANY.ID would not count in our example)

      +

      +

      +

      Inserting data

      +

      The interface for inserting data in a Relation is the same as the one that is +being used for Table. However, because rows are being inserted in a relation +one-to-many, things internally work a bit differently.

      +

      The Relation insert() method takes an optional argument, which can be +'complete' or 'abort' (default being complete).

      +

      insert() splits the relation columns into separate records that can be inserted +in a single table. However, some of the records may exist already!

      +

      for example, if we perform:

      +
      +    $sql = shift; # our GT::SQL object
      +    $rel = $sql->table(qw/EMPLOYEE COMPANY/);
      +    $rel->insert({
      +        'EMPLOYEE.NAME'   => $your_name,
      +        'EMPLOYEE.SALARY' => $big_buck,
      +        'COMPANY.NAME'    => "Gossamer Threads"
      +    });
      +

      Obviously the company ``Gossamer Threads'' already exists, but you were not in +the ``EMPLOYEE'' table. Thus, when 'complete' is specified (it is the default +option), the program will not complain if a record to insert already exists but +just warns and continue the insertion work.

      +

      In other words, Gossamer Threads exists already and it will not be inserted +twice, but the employee will still be inserted and will belong to this company.

      +

      On the other hand, if you specify ``abort'', then no data is inserted if a +record that has to be inserted would trigger an error in GT::SQL::Table.

      +

      This feature can be useful if you want to insert a relation record assuming +that none of the entities that you specify should exist.

      +

      +

      +

      Deleting data

      +

      Deleting data from a Relation object works using the following pattern:

      +
      +    .                                                        .
      +    . for each row that matches the delete condition         .
      +    . do                                                     .
      +    .   split the row in table-based records                 .
      +    .   for each table that contains foreing keys from the   .
      +    .       current relation object                          .
      +    .   do                                                   .
      +    .     delete the record                                  .
      +    .   end-do                                               .
      +    .                                                        .
      +    .   for each table that is being referenced by another   .
      +    .       table in the current relation object             .
      +    .   do                                                   .
      +    .     delete the record unless there exists              .
      +    .     some "referencing" data.                           .
      +    .   end-do                                               .
      +    .                                                        .
      +

      As I feel that this explanation is probably very confusing, let us see how it +works using our classical example (The salary column has been removed).

      +
      +  .-------------------------------------------------------------.
      +  | EMPLOYEE.ID | COMPANY_ID | EMPLOYEE.NAME | COMPANY.NAME     |
      +  `-------------------------------------------------------------'
      +  | 1           | 1          | Alex          | Gossamer Threads |
      +  |-------------|------------|---------------|------------------|
      +  | 2           | 1          | Scott         | Gossamer Threads |
      +  |-------------|------------|---------------|------------------|
      +  | 3           | 1          | Aki           | Gossamer Threads |
      +  `-------------------------------------------------------------'
      +

      Now let us say that we do the following:

      +
      +  # remove all the crazy geeks
      +  $relation->delete({ 'EMPLOYEE.NAME' => 'Scott' });
      +

      This will remove ``Scott'' from the EMPLOYEE table, but of course +Gossamer Threads will not be deleted because there still exists Alex and Aki +that would reference it.

      +

      Now if we do:

      +
      +  $relation->delete({ 'COMPANY.NAME' => 'Gossamer Threads' });
      +

      or even

      +
      +  my $condition = new GT::SQL::Condition;
      +  $condition->add(qw/EMPLOYEE.NAME LIKE %/);
      +  $relation->delete($condition);
      +

      Then we have generated a condition that matches all the employees, this means +that when the last record will be deleted, then the company Gossamer Threads +will have no more employees and therefore will be deleted.

      +

      (Yeah, well, this is for the purpose of this example, of course this will never +happen in real life :) )

      +

      +

      +

      Updating records

      +

      Currently, there exists a limitation on updating records in a Relation, which +is that only the records that represent the ``many'' part of the Relation are +updated.

      +

      The way it proceeds to perform the update is pretty simple:

      +
      +    .                                                        .
      +    . for each row that matches the update condition         .
      +    . do                                                     .
      +    .   split the row in table-based records                 .
      +    .   for each table that contains foreing keys from the   .
      +    .       current relation object                          .
      +    .   do                                                   .
      +    .     update the record                                  .
      +    .   end-do                                               .
      +    .                                                        .
      +

      That means that this will work:

      +
      +  # SALARY being a property of EMPLOYEE, it will be updated
      +  # because EMPLOYEE references COMPANY and therefore is a
      +  # "many"
      +  $relation->update({ SALARY => $big_bill },
      +                    { 'COMPANY.NAME' => 'Gossamer Threads' });
      +
      +  # nope, you cannot use Relation to update the COMPANY table that
      +  # way, this will not do anything.
      +  $relation->update({ 'COMPANY.NAME' => 'New_Name' },
      +                    { 'COMPANY.NAME' => 'Gossamer Threads' });
      +

      Who would like to change such a great name anyway ?

      +

      +

      +

      Selecting Records

      +

      Select behaves exactly like the GT::SQL::Table manpage select. The only difference is +the ability to specify LEFT JOINs. For instance, if you want to see a list of +Employees who don't belong to a company, you can do:

      +
      +    my $relation = $DB->table('Employees', 'Company');
      +    my $cond = GT::SQL::Condition->new('Company.ID', 'IS', \'NULL');
      +    my $sth = $relation->select('left_join', $cond);
      +

      The order of tables specified in the relation constructor is important!

      +

      In selecting columns, calling functions utilizing fully qualified column names +will cause GT::SQL::Relation to fail. Simply turn the values into references +like below.

      +
      +    my $sth = $relation->select("MIN(Company.ID)"); # will fail
      +
      +    my $sth = $relation->select(\"MIN(Company.ID)"); # will work
      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Relation.pm,v 1.102 2004/08/28 03:53:43 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/SQL/Search.html b/site/glist/templates/help/GT/SQL/Search.html new file mode 100644 index 0000000..b5f4a84 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Search.html @@ -0,0 +1,791 @@ + + + +GT::SQL::Search - internal driver for searching + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::SQL::Search - internal driver for searching

      +

      +

      +
      +

      SYNOPSIS

      +

      This implements the query string based searching scheme for GT::SQL. Driver +based, it is designed to take advantage of the different indexing schemes +available on different database engines.

      +

      +

      +
      +

      DESCRIPTION

      +

      Instead of describing how Search.pm is interfaced* this will describe how a +driver should be structured and how a new driver can be implemented.

      +

      * as it is never accessed directly by the programmer as it was designed to be +called through the functions GT::SQL::Table::query and GT::SQL::Table::query_sth

      +

      +

      +

      Drivers

      +

      A driver has two parts. The Indexer and the Search packages are the most +important. Howserver, for any driver in the search, there must exist a directory +with the name of the driver in ALL CAPS. For exampel, MYSQL for MySQL, POSTGRES +for Postgres. Within each driver directory, The Indexer and Search portions of +the driver contains all the information required for initializing the database +table and searching the database.

      +

      The Indexing package of the driver handles all the data that is manipulated in +the database and also the initializes and the database for indexing.

      +

      The Search package handles the queries and retrieves results for the eventual +consumption by the calling program.

      +

      Drivers are simply subclasses of the base driver module, GT::SQL::Search::Base +and operate by overriding certain key functions.

      +

      The next few sections will cover how to create a search driver, and assumes a +fair bit of familiarity with GT::SQL.

      +

      +

      +

      Structure of an Indexing Driver

      +

      The following is an absolutely simple skeleton driver that does nothing and but +called ``CUSTOM''. Found in the CUSTOM directory, this is the search package, and +would be call Search.pm in the GT/SQL/Search/CUSTOM library directory.

      +
      +    package GT::SQL::Search::CUSTOM::Search;
      +    #------------------------------------------
      +        use strict;
      +        use vars qw/ @ISA /;
      +        use GT::SQL::Search::Base::Search;
      +        @ISA = qw( GT::SQL::Search::Base::Search );
      +    
      +    sub load { my $package_name = shift; return GT::SQL::Search::CUSTOM::Search->new(@_) };
      +    
      +    # overrides would go here
      +    
      +    1;
      +

      For the indexer, another file, Indexer.pm would be found in the +GT/SQL/Search/CUSTOM directory.

      +
      +    package GT::SQL::Search::CUSTOM::Indexer;
      +    #------------------------------------------
      +    
      +        use strict;
      +        use vars qw/ @ISA /;
      +        use GT::SQL::Search::Base;
      +        @ISA = qw/ GT::SQL::Search::Base::Indexer /;
      +    
      +    sub load { my $package_name = shift; return GT::SQL::Search::CUSTOM::Indexer->new(@_) };
      +    
      +    # overrides would go here
      +    
      +    1;
      +

      The almost empty subs that immediately return with a value are functions that +can be overridden to do special tasks. More will be detailed later.

      +

      The Driver has been split into two packages. The original package name, +GT::SQL::Search::Nothing, houses the Search package. +GT::SQL::Search::Nothing::Indexer is the Indexing portion of the seach system. +``::Indexer'' must be appended to the orginial search name for the indexer.

      +

      Each of the override functions are triggered at points just before and after a +major event occurs in GT::SQL. Depending on the type of actions you require, you +pick and chose which events you'd like your driver to attach to.

      +

      +

      +

      Structure of Indexing Driver

      +

      The Indexer is responsible for creating all the indexes, maintaining them and +when the table is dropped, removing all the associated indexes.

      +

      The following header must be defined for the Indexer. +GT::SQL::Search::Base::Indexer is the superclass that our driver inherits from.

      +
      +    package GT::SQL::Search::CUSTOM::Indexer;
      +    #------------------------------------------
      +    
      +        use strict;
      +        use vars qw/ @ISA /;
      +        use GT::Base;
      +        use GT::SQL::Search::Base::Indexer;
      +        @ISA = qw/ GT::SQL::Search::Base::Indexer /;
      +

      In addition to the header, the following function must be defined. +GT::SQL::Search::Driver::Indexer::load creates the new object and allows for +special preinitialization that must occur. You can also create another driver +silently (such as defaulting to INTERNAL after a version check fails).

      +
      +    sub load { my $package_name = shift; return GT::SQL::Search::CUSTOM::Indexer->new(@_) };
      +

      Finally, there are the overrides. None of the override functions need be defined +in your driver. Any calls made to undefined methods will silently fallback to +the superclass driver's methods. When a method has been overridden, the function +must return a true value when it is successful, otherwise the action will fail +and an error generated.

      +

      Whenever a object is created it will receive one property $self->{table} which +is the table that is being worked upon. This property is available in all the +method calls and is required for methods such as _create_table and +_drop_search_driver methods.

      +

      When a table is first created or when a table is destroyed the following two +functions are called. They are not passed any special values, however, these are +all class methods and $self->{table} will be a reference to the current table in +use.

      +

      This set of overrides are used by GT::SQL::Creator when the ::create method is +called. They are called just prior and then after the create table sql query has +been executed.

      +
      +
      pre_create_table
      +
      +
      post_create_table
      +
      +
      +These functions receive no special parameters. They will receive the data to the +table in the $self->{table} property. +
      +

      +

      This next set of functions take place in GT::SQL::Editor.

      +
      +
      drop_search_driver
      +
      +
      +This method receives no special parameters but is responsible for removing all +indexes and ``things'' associated with the indexing schema. +
      +

      +
      add_search_driver
      +
      +
      +Receives no extra parameters. Creates all indexes and does all actions required +to initialize indexing scheme. +
      +

      +
      pre_add_column
      +
      +
      post_add_column
      +
      +
      +The previous two functions are called just before and after a new column is +added. +
      +
      +

      pre_add_column accepts $name (of column), $col (hashref of column attributes). +The method will only be called if the column has a weight associated with it. +The function must return a non-zero value if successful. Note that the returned +value will be passed into the post_add_column so temporary values can be passed +through if required.

      +
      +
      +

      post_add_column accepts $name (of column), $col (hashref of column attributes), +$results (of pre_add_column). This method is called just after the column has +been inserted into the database.

      +
      +

      +
      pre_delete_column
      +
      +
      post_delete_column
      +
      +
      +These previous functions are called just before and after the sql for a old +column is deleted. They must remove all objects and ``things'' associated with a +particular column's index. +
      +
      +

      pre_delete_column accepts $name (of column), $col (hashref of column +attributes). The method will only be called if the column has a weight +associated with it. The function must return a non-zero value if successful. +Note that the returned value will be passed into the post_delete_column so +temporary values can be passed through if required.

      +
      +
      +

      post_delete_column accepts $name (of column), $col (hashref of column +attributes), $results (of pre_add_column). This method is called just after the +column has been dropped from the database.

      +
      +

      +
      pre_drop_table
      +
      +
      post_drop_table
      +
      +
      +The two previous methods are used before and after the table is dropped. The +methods must remove any tables or ``things'' related to indexing from the table. +
      +
      +

      pre_drop_table receives no arguments. It can find a copy of the current table +and columns associated in $self->{table}.

      +
      +
      +

      post_drop_table receives one argument, which is the result of the +pre_drop_table.

      +
      +

      +

      The following set of functions take place in GT::SQL::Table

      +
      +
      pre_add_record
      +
      +
      post_add_record
      +
      +
      +Called just before and after an insert occurs. These functions take the record +and indexes them as required. +
      +
      +

      pre_add_record will receive one argument, $rec, hashref, which is the record +that will be inserted into the database. Table information can be found by +accessing $self->{table} Much like the other functions, on success the result +will be cached and fed into the post_add_record function.

      +
      +
      +

      post_add_record receives $rec, a hashref to describing the new result, the $sth +of the insert query, and the result of the pre_add_record method. The result +from $sth->insert_id if there is a ai field will be the new unique primary key.

      +
      +

      +
      pre_update_record
      +
      +
      post_update_record
      +
      +
      +Intercepts the update request before and just after the sql query is executed. +This override has the potential of being rather messy. More than one record can +be modified in this action and the indexer must work a lot to ensure the +database is up to snuff. +
      +
      +

      pre_update_record receives two parameters, $set_cond, $where_cond. $set_cond is +a hashref containing the new values that must be set, and $where_cond is a +GT::SQL::Condition object selecting records to update. The result once again, is +cached and if undef is considered an error.

      +
      +
      +

      post_update_record takes the same parameters as pre_update_record, except one +extra paremeter, the result of pre_update_record.

      +
      +

      +
      pre_delete_record
      +
      +
      post_delete_record
      +
      +
      +Called just before and after the deletion request for records are called. +
      +
      +

      pre_delete_record, has only one parameter, $where, a GT::SQL::Condition object +telling which records to delete. The results of this method are passed to +post_delete_record.

      +
      +
      +

      post_delete_record, has one addition parameter to pre_delete_record and like +most post_ methods, is the result of the pre_delete_record method.

      +
      +

      +
      pre_delete_all_records
      +
      +
      post_delete_all_records
      +
      +
      +These two functions are quite simple, but they are different from drop search +driver in that though the records are all dropped, the framework for all the +indexing is not dropped as well. +
      +
      +

      Neither function is passed any special data, except for post_delete_all_records +which receives the rsults of the pre_delete_all_records method.

      +
      +

      +
      reindex_all
      +
      +
      +This function is sometimes called by the user to refresh the index. The +motivation for this, in the case of the INTERNAL driver, is sometimes due to +outside manipulation of the database tables, the index can become +non-representative of the data in the tables. This method is to force the +indexing system to fix errors that have passed. +
      +

      +
      ok
      +
      +
      +This function is called by GT::SQL::Search as a package method, +GT::SQL::Search::EXAMPLE::Indexer->ok( $table ); and is passed a table object +reference. What this function must do is to return a true or false value that +tells the search system if this driver can be used. The MYSQL driver has a good +example for this, it tests to ensure that the mysql database system version is +at least 3.23.23. +
      +

      +

      +

      +

      Structure of a Search Driver

      +

      The Searcher is responsible for only one thing, to return results from a query +search. You can override the parser, however, subclassing the following methods +will have full parsing for all things such as +/-, string parsing and substring +matching.

      +

      The structures passed into the methods get a little complicated so beware!

      +

      ALL the following functions receive two parameters, the first is a search +parameters detailing the words/phrases to search for, the second parameter is +the current result set of IDs => scores.

      +

      There are two types of search parameters, one for words and the other for +phrases. The structure is a little messy so I'll detail them here.

      +

      For words, the structure is like the following:

      +
      +    $word_search = {
      +        'word' => {
      +            substring => '1', # set to 1 if this is substring match
      +            phrase    => 0,   # not a phrase
      +            keyword   => 1,   # is a keyword
      +            mode      => '',  # can also be must, cannot to mean +/-
      +        },
      +        'word2' => ...
      +    }
      +

      For phrases the structure will become:

      +
      +    $phrase_search => {
      +        'phrase' => {
      +            substring => undef # never required
      +            phrase    => [
      +                'word1',
      +                'word2',
      +                'word3',
      +                ...
      +            ],              # for searching by indiv word if required
      +            keyword   => 0, # not a keyword
      +            mode      => ''    # can also be must, cannot
      +        },
      +        'phrase2' => ...
      +    }
      +

      Based on these structures, hopefully it will be easy enough to build whatever is +required to grab the appropriate records.

      +

      Finally, the second item passed in will be a hash filled with ID => score values +of search results. They look something like this:

      +
      +    $results = {
      +        1 => 56,
      +        2 => 31,
      +        4 => 6
      +    }
      +

      It is important for all the methods to take the results and return the results, +as the result set will be daisychained down like a set to be operated on by +various searching schemes.

      +

      At the end of the query, the results in this set will be sorted and returned to +the user as an sth.

      +

      Operations on this set are preformed by the following five methods.

      +
      +
      _query
      +
      +
      +This method is called just after all the query string has been parsed and put +into their proper buckets. This method is overridden by the INTERNAL driver to +decide it wants to switch to the NONINDEX driver for better performance. +
      +
      +

      Two parameters are passed in, ( $input, $buckets ). $input is a hash that +contains all the form/cgi parameters passed to the $tbl->query function and +$buckets is s the structure that is created after the query string is parsed. +You may also call $self->SUPER::_query( $input, $buckets ) to pass the request +along normally.

      +
      +
      +

      You must return undef or an STH from this function.

      +
      +

      +
      _union_query
      +
      +
      +This method takes a $word_search and does a simple match query. If it finds +records with any of the words included, it will append the results to the list. +Passed in is the $results and it must return the altered results set. +
      +
      +

      This method must also implement substring searching.

      +
      +

      +
      _phrase_query
      +
      +
      +Just like the union_query, however it searches based on phrases. +
      +

      +
      _phrase_intersect_query
      +
      +
      +This takes a $phrase_search and a $result as parameters. This method must look +to find results that are found within the current result set that have the +passed phrases as well. However, if there are no results found, this method can +look for more results. +
      +

      +
      _intersect_query
      +
      +
      +Takes two parameters, a $word_search, and $results. Just like the +_phrase_intersect query, if there are results already, tries to whittle away the +result set. If there are no results, tries to look for results that have all the +keywords in a record. +
      +
      +

      This method must also implement substring searching.

      +
      +

      +
      _disjoin_query
      +
      +
      +Takes two parameters, a $word_search, and $results. This will look through the +result set and remove all matches to any of the keywords. +
      +
      +

      This method must also implement substring searching.

      +
      +

      +
      _phrase_disjoin_query
      +
      +
      +Two parameters, $phrase_search and $results are passed to this method. This does +the exact same thing as _disjoin_query but it looks for phrases. +
      +

      +
      query
      +
      +
      +If you choose to override this method, you will have full control of the query. +
      +
      +

      This method accepts a $CGI or a $HASH object and performs the following

      +
      +
      +
      +  Options:
      +         - paging
      +            mh            : max hits
      +            nh            : number hit (or page of hits)
      +            sb            : column to sort by (default is by score)
      +
      +
      +
      +         - searching
      +            ww            : whole word
      +            ma            : 1 => OR match, 0 => AND match, undefined => QUERY
      +            substring     : search for substrings of words
      +            bool          : 'and' => and search, 'or' => or search, '' => regular query
      +            query         : the string of things to ask for
      +
      +
      +
      +         - filtering
      +            field_name    : value       # Find all rows with field_name = value
      +            field_name    : ">value"    # Find all rows with field_name > value.
      +            field_name    : "<value"    # Find all rows with field_name < value.
      +            field_name-gt : value       # Find all rows with field_name > value.
      +            field_name-lt : value       # Find all rows with field_name < value.
      +
      +
      +

      The function must return a STH object. However, you may find useful the +GT::SQL::Search::STH object, which will automatically handle mh, nh, and +alternative sorting requests. All you will have to do is

      +
      +
      +
      +    sub query { ... your code ... return $self->sth( $results ); }
      +
      +
      +

      Where results is a hashref containing primarykeyvalue => scorevalues.

      +
      +

      +
      alternate_driver_query
      +
      +
      +There is no reason to override this method, however, if you would like to use +another driver's search instead of the current, this method will let you do so. +
      +
      +

      Accepting 2 parameters, ( $drivername, $input ), where $drivername is the name +of the driver you'd like to use and $input is the parameters passed to the +method. Returned is an $sth value (undef if an error has occured). This method +was used in the INTERNAL driver to shunt to NONINDEXED if it found the search +would take too long.

      +
      +

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Search.pm,v 1.60 2004/08/28 03:53:43 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/SQL/Search/Base/Common.html b/site/glist/templates/help/GT/SQL/Search/Base/Common.html new file mode 100644 index 0000000..6baf8f5 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Search/Base/Common.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Search/Base/Common.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Search/Base/Indexer.html b/site/glist/templates/help/GT/SQL/Search/Base/Indexer.html new file mode 100644 index 0000000..cb34dae --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Search/Base/Indexer.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Search/Base/Indexer.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Search/Base/STH.html b/site/glist/templates/help/GT/SQL/Search/Base/STH.html new file mode 100644 index 0000000..39c9ab9 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Search/Base/STH.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Search/Base/STH.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Search/Base/Search.html b/site/glist/templates/help/GT/SQL/Search/Base/Search.html new file mode 100644 index 0000000..c034258 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Search/Base/Search.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Search/Base/Search.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Search/INTERNAL/Indexer.html b/site/glist/templates/help/GT/SQL/Search/INTERNAL/Indexer.html new file mode 100644 index 0000000..f67d5b1 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Search/INTERNAL/Indexer.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Search/INTERNAL/Indexer.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Search/INTERNAL/Search.html b/site/glist/templates/help/GT/SQL/Search/INTERNAL/Search.html new file mode 100644 index 0000000..4bc9d60 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Search/INTERNAL/Search.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Search/INTERNAL/Search.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Search/MSSQL/Indexer.html b/site/glist/templates/help/GT/SQL/Search/MSSQL/Indexer.html new file mode 100644 index 0000000..4f62635 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Search/MSSQL/Indexer.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Search/MSSQL/Indexer.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Search/MSSQL/Search.html b/site/glist/templates/help/GT/SQL/Search/MSSQL/Search.html new file mode 100644 index 0000000..84124f9 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Search/MSSQL/Search.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Search/MSSQL/Search.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Search/MYSQL/Indexer.html b/site/glist/templates/help/GT/SQL/Search/MYSQL/Indexer.html new file mode 100644 index 0000000..38749b8 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Search/MYSQL/Indexer.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Search/MYSQL/Indexer.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Search/MYSQL/Search.html b/site/glist/templates/help/GT/SQL/Search/MYSQL/Search.html new file mode 100644 index 0000000..dceef60 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Search/MYSQL/Search.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Search/MYSQL/Search.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Search/MYSQL/VER3.html b/site/glist/templates/help/GT/SQL/Search/MYSQL/VER3.html new file mode 100644 index 0000000..153c9be --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Search/MYSQL/VER3.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Search/MYSQL/VER3.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Search/MYSQL/VER4.html b/site/glist/templates/help/GT/SQL/Search/MYSQL/VER4.html new file mode 100644 index 0000000..682b861 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Search/MYSQL/VER4.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Search/MYSQL/VER4.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Search/NONINDEXED/Indexer.html b/site/glist/templates/help/GT/SQL/Search/NONINDEXED/Indexer.html new file mode 100644 index 0000000..531950f --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Search/NONINDEXED/Indexer.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Search/NONINDEXED/Indexer.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Search/NONINDEXED/Search.html b/site/glist/templates/help/GT/SQL/Search/NONINDEXED/Search.html new file mode 100644 index 0000000..51c6a5c --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Search/NONINDEXED/Search.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Search/NONINDEXED/Search.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/SQL/Table.html b/site/glist/templates/help/GT/SQL/Table.html new file mode 100644 index 0000000..2fa0cc3 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Table.html @@ -0,0 +1,830 @@ + + + +GT::SQL::Table - a perl interface to manipulate a single SQL table. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::SQL::Table - a perl interface to manipulate a single SQL table.

      +

      +

      +
      +

      SYNOPSIS

      +
      +    my $sth = $table->select(Column3 => { Column => $value, Column2 => $value2 });
      +    $table->delete({ Column => $value });
      +    $table->insert({ Column1 => $val, Column2 => $value2 });
      +    $table->update({ SetCol => $val }, { WhereCol => $val2 });
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::SQL::Table provides methods to add, modify, delete and search over a single +SQL table.

      +

      The following methods are provided.

      +

      +

      +

      query, query_sth

      +

      query provides a simple and powerful method to search a table. It takes as +input either a hash, hash ref or CGI object making it especially useful +searching from web forms.

      +
      +    my $results = $db->query($in);
      +

      The return of query is an arrayref of arrayrefs. query_sth returns an STH +that you can fetch rows from.

      +

      Typical usage to go through the results is:

      +
      +    my $results = $db->query({ Title => 'foobar' });
      +    if ($results) {
      +        for my $result (@$results) {
      +            ...
      +        }
      +    }
      +

      To specify what to search, you simply pass in column => search value. However, +you can also pass in a lot of options to enhance your search:

      +

      Find all rows with field_name = value:

      +
      +    field_name    => value
      +

      Find all rows with field_name > value:

      +
      +    field_name    => ">value"
      +

      Find all rows with field_name < value:

      +
      +    field_name    => "<value"
      +

      Find all rows with field_name > value:

      +
      +    field_name-gt => value
      +

      Find all rows with field_name < value:

      +
      +    field_name-lt => value
      +

      Find all rows where any field_name = value:

      +
      +    keyword       => value
      +

      Find all rows using indexed search (see weights):

      +
      +    query         => value
      +

      Set to 1, use '=' comparison, 0/unspecified use 'LIKE '%val%' comparision:

      +
      +    ww            => 1
      +

      Search using LIKE for column 'Title' (valid opts are '=', '>', '<' or 'LIKE'):

      +
      +    Title-opt     => 'LIKE'
      +

      Set to 1, OR match results, 0/unspecified AND match results:

      +
      +    ma            => 1
      +

      Return a max of n results, defaults to 25:

      +
      +    mh            => n
      +

      Return page n of results:

      +
      +    nh            => n
      +

      Sort by 'Title' column:

      +
      +    sb            => 'Title'
      +

      Sort in ascending (ASC) or descending (DESC) order:

      +
      +    so            => 'ASC'
      +

      +

      +

      select

      +

      Select provides a way to implement almost any sql SELECT statement.

      +

      An executed statement handle is returned that you can call the normal fetchrow, +fetchrow_array, fetchrow_hashref, etc on.

      +
      +    my $sth = $obj->select;
      +

      is equivalant to ``SELECT * FROM Table''

      +
      +    my $sth = $obj->select({ Col => Val });
      +

      is equivalant to ``SELECT * FROM Table WHERE Col = 'Val'''.

      +
      +    my $sth = $obj->select('Col2', 'Col3', { Col => "Val" });
      +

      is equivalant to ``SELECT Col2,Col3 FROM Table WHERE Col => 'Val'''.

      +

      So you can pass in a hash reference which represents the where clause, and an +array reference where represents what you want to select on.

      +

      If you need more complex where clauses, you should use a condition object +instead of a hash reference. See the GT::SQL::Condition manpage for more information.

      +

      Notes:

      +
      +
      quoting in where
      +
      +
      +All arguments in the where clause are automatically quoted. If you don't want +quotes, you should pass in a scalar reference as in: +
      +
      +
      +    my $sth = $obj->select({ Col => \"NOW()" });
      +
      +
      +

      which turns into ``SELECT * FROM Table WHERE Col = NOW()''.

      +
      +

      +
      quoting in select
      +
      +
      +Nothing in the select will be quoted, so to use functions, simply pass in what +you want: +
      +
      +
      +    my $sth = $obj->select('COUNT(*)');
      +
      +
      +

      which turns into ``SELECT COUNT(*) FROM Table''.

      +
      +

      +

      To specify LIMIT, or GROUP BY, or ORDER BY or other SELECT clauses that come +after the WHERE, you should use select_options below.

      +

      +

      +

      select_options

      +

      This method provides a way for you to specify select options such as LIMIT and +SORT_BY.

      +
      +    $obj->select_options(@OPTIONS);
      +

      @OPTIONS should be a list of options you want appended to your next select.

      +

      For example,

      +
      +    $obj->select_options('ORDER BY Foo', 'LIMIT 50');
      +    $obj->select;
      +

      would turn into ``SELECT * FROM Table ORDER BY Foo LIMIT 50''. To perform a +LIMIT with an OFFSET, you should specify something like:

      +
      +    $obj->select_options('LIMIT 25 OFFSET 75');
      +

      You can alternatively use the equivelant MySQL-specific syntax:

      +
      +    $obj->select_options('LIMIT 75, 25');
      +

      Both will be handled correctly regardless of the database type.

      +

      +

      +

      count

      +

      This method will allow you to count records based on a where clause.

      +
      +    my $count = $obj->count($condition);
      +

      count() takes either a condition or a hash reference. If no argument is +provided, it is equivalant to ``SELECT COUNT(*) FROM Table'', or total number of +rows.

      +

      +

      +

      hits

      +

      This method returns the number of hits from that last select query without +the limit clause if there was one.

      +
      +    $hits = $obj->hits;
      +

      For example, to get rows 20-30 of a query result, use:

      +
      +    $obj->select_options("LIMIT 10 OFFSET 20"); $obj->select({ Column => 'Foo' });
      +

      this translates into (in MySQL):

      +
      +    SELECT * FROM Table WHERE Column = 'Foo' LIMIT 20, 10
      +

      To see the total number of results that the query would have retrieved without +any limit, you call:

      +
      +    $hits = $obj->hits;
      +

      If the number of hits can be calculated, it will be returned to you without any +additional query. Otherwise, the following query will be performed +automatically, and the hit count returned to you:

      +
      +    SELECT COUNT(*) FROM Table WHERE Column = 'Foo'
      +

      NOTE: The hits() method _only_ applies to select queries. Most databases do +not provide enough information to get counts of rows affected for other types +of queries.

      +

      +

      +

      get

      +

      This method allows for a simple interface to retrieving records from the +table(s).

      +
      +    my $rec_hash_ref  = $obj->get($val);
      +    my $rec_hash_ref  = $obj->get($val, 'HASH', ['col1', 'col2']);
      +    my $rec_array_ref = $obj->get($val, 'ARRAY');
      +

      The first argument is the primary key value of the record you want to retrieve.

      +

      The second argument is a format option. It can be either 'ARRAY' or 'HASH' and +determines whether you are returned a HASH reference or an ARRAY reference. The +default is 'HASH', and it is optional.

      +

      The last argument is a list of column names you want retrieved. get defaults +to returning the entire record, but if you only need specific columns, you can +ask for the ones you want.

      +

      For example:

      +
      +    my $employee = $emp_db->get('Alex');
      +

      would return a hash ref of the record whose primary key is equal to 'Alex'.

      +
      +    my $emp_addr = $emp_db->get('Alex', 'HASH', ['City', 'State', 'ZipCode']);
      +

      would return a hash ref of only the three fields City, State, ZipCode for the +record whose primary key equals Alex.

      +

      +

      +

      add

      +

      Method to add an entry into the database. This method can take it's arguments +one of three ways.

      +
      +    $obj->add($CGI_OBJECT);
      +
      +    -or-
      +
      +    $obj->add({
      +        col1 => $val1,
      +        col2 => $val2,
      +        ...
      +    });
      +
      +    -or-
      +
      +    $obj->add(
      +        col1 => $val1,
      +        col2 => $val2,
      +        ...
      +    );
      +

      This method can take a cgi object, a hash reference or a hash. The keys of the +hash should be the names of the column and the values should be the values to +insert into the fields. The CGI Object is not different. If the table has an +auto_increment field, the value of the last inserted record will be returned.

      +

      add returns undef on failure. If successful, and the table has an +auto-increment field, the auto increment value is returned. If there is no +auto increment value, then 1 is returned. Any errors will be in +$GT::SQL::error.

      +

      Passing in GT_SQL_SKIP_CHECK => 1 will have the table module skip any error +checking it should perform.

      +

      Passing in GT_SQL_SKIP_INDEX => 1 will not index the fields. You can also use +the indexing method to do this.

      +

      +

      +

      insert

      +

      insert is a lower level add. The main differences between add and +insert are that add performs a not null check, and add returns the id of the +just inserted value.

      +

      insert does not perform a not null check. Also, insert returns the statement +handle used to do the insert (so you can call $sth->insert_id to get the auto +increment).

      +

      +

      +

      insert_multiple

      +

      insert_multiple will try to optimize the insertion of multiple rows with +simple values. Under MySQL, this uses MySQL's extended insert syntax:

      +
      +    INSERT INTO Table (col1, col2, col3)
      +    VALUES ('val1', 'val2', 'val3'), ('val4', 'val5', 'val6'), ...
      +

      On other databases, it attempts to perform all insertions in a single +transaction, which will also usually yield performance benefits. Note, +however, that insert_multiple should not be used for anything more complex +than basic column values - for example, inserting NULL to set the current date, +or using raw SQL by passing scalar references for values.

      +

      It takes at least two arguments - the first argument is an array ref of column +names, and the rest are array references of values. For example, to produce +the above example SQL code, you would call:

      +
      +    $table->insert_multiple(
      +        ['col1', 'col2', 'col3'],
      +        ['val1', 'val2', 'val3'],
      +        ['val4', 'val5', 'val6'],
      +        ...
      +    );
      +

      +

      +

      modify

      +

      This method is designed for modifying a single entry in the table. It takes as +input a hash, hash ref or CGI object, which is assumed to represent a single +row with all fields intact.

      +

      modify will then look for the primary key in the input and set all fields +for that row equal to what was passed in.

      +

      You need to pass in a complete record! If you just want to update one column, +you probably want to use update instead, as doing:

      +
      +    my $result = $obj->modify(column1 => 'Foo');
      +

      will blank out all the other fields and set just column1 to Foo.

      +

      modify returns undef on failure, 1 on success. The error message will be +available in $GT::SQL::error.

      +

      +

      +

      update

      +

      This method provides a more robust way to update multiple entries in the table.

      +
      +    my $result = $obj->update(
      +        {
      +            col1 => $val1,
      +            col2 => $val2,
      +            ...
      +        },
      +        $condition
      +    );
      +
      +    -or-
      +
      +    my $result = $obj->update(
      +        {
      +            col1 => $val1,
      +            col2 => $val2,
      +            ...
      +        },
      +        {
      +            col1 => $val1,
      +            col2 => $val2,
      +            ...
      +        }
      +    );
      +

      In both these cases the first argument is a hash reference with the column +names as the keys and the new values you want the columns to hold as the +values. The second argument can either be a condition object or a hash +reference. If it is a hash reference the keys will be used as the column names +and the values will be taken as the current column values for the where clause +to update the table.

      +
      +    $obj->update({ Setme => 'NewValue'}, { WhereCol => 5 });
      +

      would set the column 'Setme' to 'NewValue' where the column 'WhereCol' is 5. +This translates to:

      +
      +    UPDATE Table SET SetMe='NewValue' WHERE WhereCol = 5
      +

      If the second argument is a GT::SQL::Condition object the condition object will +be used to build the where clause with. Please see the GT::SQL::Condition manpage for a +description of what you can do with a where clause.

      +
      +    my $condition = GT::SQL::Condition->new('WhereCol', 'LIKE', 'Foo%');
      +    $obj->update({ Setme => 'Newvalue' }, $condition);
      +

      would translate to:

      +
      +    UPDATE Table SET Setme = 'Newvalue' WHERE WhereCol LIKE 'Foo%'
      +

      The condition can now much more complex where clauses though.

      +

      update returns undef on failure and the a the GT::SQL::Driver manpage statement +handle on success. The error message will be available in $GT::SQL::error.

      +

      Passing in GT_SQL_SKIP_CHECK => 1 as a third option to update will have the +table module skip any error checking it should perform.

      +

      Passing in GT_SQL_SKIP_INDEX => 1 will not index the fields. You can also use +the indexing method to do this.

      +

      +

      +

      delete

      +

      This method provides a robust interface to delete entries from your table(s) +using join and or foreign key relations.

      +
      +    my $result = $obj->delete($condition);
      +

      You can pass into delete either a condition object to delete multiple +entries, or a scalar value to delete the row whose primary key equals the +value. If you have a multiple primary key, then you can pass in an array ref to +delete that row.

      +
      +    my $result = $obj->delete({
      +        col1 => $val1,
      +        col2 => $val2,
      +        ...
      +    );
      +
      +    -or-
      +
      +    $obj->delete($val);
      +
      +    -or-
      +
      +    $obj->delete([$val1, $val2]);
      +

      delete returns undef on failure, 1 on success. The error message will be +available in $GT::SQL::error.

      +

      +

      +

      delete_all

      +

      This method takes no arguments and will erase all entries from a table.

      +

      +

      +

      Table Properties

      +

      Table provides a lot of methods to access information about the table:

      +
      +
      name
      +
      +
      +Provides the name of the table minus any prefix. +
      +

      +
      ai
      +
      +
      +Returns the name of the auto-increment field if any. +
      +

      +
      pk
      +
      +
      +Returns an array(ref) of primary key column names. +
      +

      +
      fk
      +
      +
      +Returns a hash of foreign key values. +
      +

      +
      fk_tables
      +
      +
      +Returns a list of tables with foreign keys pointing to this table. +
      +

      +
      index
      +
      +
      +Returns a hash ref of index name => array ref of column names that index uses. +
      +

      +
      unique
      +
      +
      +Returns a hash ref of unique index names => array ref of column names that +unique index uses. +
      +

      +
      all_indexes
      +
      +
      +Returns the joined output of index and unique and primary key. +
      +

      +
      cols
      +
      +
      +Returns a hash(ref) of column name => column definition +
      +

      +
      default
      +
      +
      +Returns a hash(ref) of column name => default value. +
      +

      +
      size
      +
      +
      +Returns a hash(ref) of column name => size of column in SQL. +
      +

      +
      type
      +
      +
      +Returns a hash(ref) of column name => type of column in SQL. +
      +

      +
      form_display
      +
      +
      +Returns a hash(ref) of column name => name to display on auto generated forms +(think pretty name). +
      +

      +
      form_size
      +
      +
      +Returns a hash(ref) of column name => size of html form to generate. +
      +

      +
      form_type
      +
      +
      +Returns a hash(ref) of column name => type of html form to generate (checkbox, +select, text, etc). +
      +

      +
      form_names
      +
      +
      +Returns a hash(ref) of column name => array ref of form names. This is used for +multi option form elements like checkboxes and multi selects. The name is what +is displayed to the user and not entered in the database. +
      +

      +
      form_values
      +
      +
      +Returns a hash(ref) of column name => array ref of form values. Same as above, +but this is the value that actually gets entered. +
      +

      +
      time_check
      +
      +
      +Returns a hash(ref) of column name => time check on or off. If set +
      +

      +
      regex
      +
      +
      +Returns a hash(ref) of column name => regular expression that all input must +pass before being inserted. +
      +

      +
      pos
      +
      +
      +Returns a hash(ref) of column name => position in table. +
      +

      +
      not_null
      +
      +
      +Returns a hash(ref) of column name => not null (whether the field is allowed to +be null or not). +
      +

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Table.pm,v 1.251 2005/02/28 20:37:41 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/SQL/Tree.html b/site/glist/templates/help/GT/SQL/Tree.html new file mode 100644 index 0000000..860f8d3 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Tree.html @@ -0,0 +1,686 @@ + + + +GT::SQL::Tree - Helps create and manage a tree in an SQL database. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::SQL::Tree - Helps create and manage a tree in an SQL database.

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::SQL::Tree;
      +
      +    my $tree = $table->tree;
      +    my $children = $tree->children(id => [1,2,3], max_depth => 2);
      +
      +    my $parents = $tree->parents(id => [4,5,6]);
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::SQL::Tree is designed to implement a tree structure with a SQL table. Most +of the work on managing the table is performed automatically behind the scenes, +however there are a couple of front end methods to retrieving the tree nodes +from a GT::SQL::Tree object.

      +

      +

      +
      +

      METHODS

      +

      +

      +

      new, tree

      +

      Typically, the way to get a tree object is to call ->tree on a table object. The +table object then calls GT::SQL::Tree->new for you and returns the results, +which is a GT::SQL::Tree object. Typically you should not call ->new directly, +but instead let $table->tree call it with the proper arguments.

      +

      +

      +

      create, add_tree

      +

      To use GT::SQL::Tree, you need to first call create(). You shouldn't call it +directly, but instead call ->add_tree() on an editor object. The arguments to +add_tree are passed through to create, so that they are essentially the same +(there is one exception - add_tree passed in table => $table_object).

      +

      create() will create a tree table, with the name passed on the name of the table +passed in. For example, if you wish to build a tree on 'MyTable', the tree table +that is created by create() will be named MyTable_tree. The tree table provides +easy one-query access to all of a nodes parents or children, and also keeps +track of the number of hops between a node and its descendant, allowing you to +limit how far you descend into the tree.

      +

      The following arguments are required:

      +
      +
      table
      +
      +
      +This contains the table object for the table the tree is to be built upon. Note +that when calling add_tree you should not specify this - add_tree passes it +along on its own. +
      +

      +
      father
      +
      +
      +This must specify the name of the father ID column. The father ID column +controls the relationship between father/child. +
      +
      +

      For example, if your primary key is ``my_id'' and your father id column is +``my_father_id'', you would pass in ``my_father_id'' as the value to father.

      +
      +

      +
      root
      +
      +
      +This is used to specify the name of the root column. For example, if your +primary key is ``my_id'' and your root id column is ``my_root_id'', you would pass +in ``my_root_id'' as the value to root. +
      +

      +
      depth
      +
      +
      +This is used to specify the name of the depth column for the table. For example, +if you are using a column named ``my_depth'' to keep track of the depth of a node, +you would pass in ``my_depth'' as the value to depth. +
      +

      +

      The following are optional arguments to create/add_tree:

      +
      +
      force
      +
      +
      +Takes a value such as 'force' or 'check'. This value is passed on to the +GT::SQL table creation subroutine. +
      +

      +
      rebuild
      +
      +
      +You can pass in a GT::SQL::Tree::Rebuild object if you have an incomplete or +invalid table structure. See the GT::SQL::Tree::Rebuild manpage for more details. +
      +

      +
      debug
      +
      +
      +Sets the debug level of the tree object. add_tree() automatically passes in the +debug value for the table object, so it normally is not necessary to set this. +
      +

      +

      +

      +

      destroy, drop_tree

      +

      You can call $tree->destroy to destroy a tree. This involves dropping the +tree table and deleting the tree reference from the table the tree was on. This +can be called by calling $tree->destroy() on a GT::SQL::Tree object, +however this is typically invoked by calling $editor->drop_tree() on a +table editor object.

      +

      Neither $tree->destroy() nor $editor->drop_tree() take any +arguments.

      +

      +

      +

      root_id_col, father_id_co, depth_col

      +

      These three tree object methods return the name of the associated column in the +main table. Usually you will already know them, and these methods are primarily +used internally.

      +

      +

      +

      children

      +

      This is where the usefulness of the tree module comes into play. +$tree->children is used to access all of the children of a particular +node. It takes a wide variety of arguments to control the return.

      +

      Usually, the return will be either a hash reference of array references each +containing hash references, or else an array reference of hash references. Which +reference you get depends on what you request via the id parameter, described +below. Each inner hash reference is a row from the database, typically a joined +row from the table the tree is on with the tree table, however the +roots_only, cols, and select_from parameters all change this behaviour.

      +

      The arguments to children() are as follows:

      +
      +
      id
      +
      +
      +The value of the id key is either a scalar value, or an array reference. The +value/values to id should be the id whose descendants you are looking for. For +example, if you are looking for the children of ID 3 and ID 4, you would pass in +id => [3, 4]. The return value of children will be a hash reference +containing two keys: 3 and 4. +
      +
      +

      If you are looking for the children of a single ID and pass the id as a scalar +value, you will get back an array reference as described above.

      +
      +
      +

      So, basically, if the value to id is an array reference, you will get back a +hash reference of array references of hash references; if it is a scalar value, +you will get back an array reference of hash references. + $tree->children(id => [1])->{1}; +and + $tree->children(id => 1); +will result in the same thing.

      +
      +
      +

      To get all the trees in a single query, you pass in 0 as the value. This is as +if you are requesting the children of the imaginary root to which all roots +belong.

      +
      +
      +

      id is the only required parameter.

      +
      +

      +
      max_depth
      +
      +
      +You can specify a max_depth value to specify that the records returned should +not be more a certain distance from the node. For example, supposing you have +this tree: + a + b + c + d +Selecting the children of a with a max_depth of 1 would return just b, not c or +d. A max_depth of 2 would return b and c. +
      +
      +

      Not specifying max_depth means that you do not want to limit the maximum +distance from the parent of the returned values.

      +
      +

      +
      cols
      +
      +
      +You can specify an array reference as the value to cols to alter the values +returned. Instead of doing ``SELECT * FROM ...'', the query will be ``SELECT <what +you specify> FROM ...''. Note, however, that the father, root, and depth columns +are required and will be present in the rows returned whether or not you specify +them. +
      +

      +
      sort_col, sort_order
      +
      +
      +Where the sort option sorts the results based on tree levels, sort_col and +sort_order control the sorting for nodes with the same father ID. For +example, with this tree: + a + b + c +sort_col and sort_order affect whether or not b comes before or after c. +The value of each can either be a scalar value or an array reference. There is +essentially no difference, the scalar value is just a little easier when you are +only sorting on a single column. The values of sort_col should be column +names, and the values of sort_order 'ASC' or 'DESC', per sort column +respectively. For example: + sort_col => ['a','b'], sort_order => ['ASC', 'DESC'] +will sort first in ascending order based on the value of a, then descending +order based on the value of column b. This correlates directly to SQL - it +becomes ``ORDER BY a ASC, b DESC''. +
      +
      +

      You can specify a different sort order for roots by using the roots_order_by +option, when using id => 0. See below.

      +
      +

      +
      condition
      +
      +
      +If you want to limit the results, you can pass a GT::SQL::Condition object into +children() via the condition key. The condition will apply to the select +performed. For example, if you want to select rows with a column ``a'' having a +value less than 20, you could do: + my $cond = GT::SQL::Condition->new(a => '<' => 20) + my $children = $tree->children(..., condition => $cond); +
      +

      +
      limit
      +
      +
      +Like condition, you can specify any valid LIMIT _____ value here, for example +``50, 25''. This option is only used when using id => 0 - it will limit the +number of roots returned, taking into account the sort_col and sort_order. +
      +

      +
      roots_only
      +
      +
      +If you specify this option, it will assume that what you passed in via id +consists only of root_ids. Doing so makes a join with the tree table +unneccessary and allows you to use the select_from option. This option can be +used (and generally this is a good idea) when specifying id => 0. +
      +

      +
      roots_order_by
      +
      +
      +This option controlls the order of root posts, when selecting roots using +id => 0 and a limit. sort_order above will affect the order of +children of the roots, but the order of the roots themselves will be controlled +by whatever ORDER BY value you specify here. +
      +
      +

      Again, this option requires that id => 0, roots_only, and limit are +also being used.

      +
      +
      +

      If this option is omitted, the ORDER BY will be generated from the values of +the sort_col and sort_order options.

      +
      +

      +
      select_from
      +
      +
      +If you are using roots_only, you can also specify the select_from option. +This option allows you to perform the selects from a GT::SQL::Relation object +instead of just the table associated with the tree. Note that the table +associated with the tree must be part of the relation, however you can have as +many other tables as you like. +
      +

      +
      left_join
      +
      +
      +If the select_from relation should be a left join, pass left_join => 1. +This simply passes the left_join option to ->select. This option is only +applicable when select_from is used. +
      +

      +

      +

      +

      parents

      +

      This is effectively the opposite of children. Instead of getting back all of the +children nodes, it gives the parents, all the way up to the root for any given +node. The return value is the same as that of children, so see that section.

      +

      Each array returned by children is sorted by depth from root to parent.

      +
      +
      id
      +
      +
      +id is the only required parameter for parents(). It should be either a +scalar value or an array reference. You specify the ID's of children whose +parents you are looking for. The type of argument (scalar or array ref) affects +the return in the same way as children(). +
      +

      +
      cols
      +
      +
      +cols works in a similar way to the cols parameter to children. You +specify the columns you want in the return as an array ref. What you get back +will have these columns in it. If cols is not specified, you'll get back all +columns. +
      +
      +

      Note that 'tree_id_fk' and the depth column for the table are required fields +and will be added if not specified.

      +
      +

      +

      +

      +

      child_ids

      +

      If you are looking for just the ID's of the children of a particular node, you +should use this. The return value is one of the following, depending on what you +pass in:

      +

      hash reference of array references: + { ID => [ID, ID, ...], ... } +with one ID in the hash reference for each id you specify. The array reference +contains the child ID's of the key ID.

      +

      hash reference of hash references: + { ID => { ID => dist, ID => dist, ... }, ... } +with one ID in the other hash reference for each id you specify. The inner hash +reference is made of child_id => child_distance key-value pairs.

      +

      array reference or hash reference: + [ID, ID, ...] +hash reference: + { ID => dist, ID => dist }

      +

      The first two apply when passing in an array reference for id, the latter two +when passing a scalar value for id. The first and third are without +include_dist specified, the second and fourth occur when you specify +include_dist.

      +
      +
      id
      +
      +
      +Like all other accessors, child_ids takes a scalar value or array reference as +the id value. Return as noted above. +
      +

      +
      include_dist
      +
      +
      +This changes the return as noted above - instead of just getting an array +reference of child ID's, you get the child ID's as the keys of a hash reference, +and the distances of the child from the parent you requested as the values. +
      +

      +

      +

      +

      parent_ids

      +

      Exactly the same as child_ids, except that this works up the tree instead of +down. Takes the same arguments, gives the same possible returns.

      +

      +

      +
      +

      INDICES

      +

      A tree requires a few indices to get optimal performance out of it. If the table +is never expected to be more than just a few rows, you won't notice a +substantial difference, however, as with any table, as the table grows the +performance proper indexing provides becomes more appreciable.

      +

      Two indices are created automatically on the tree table, one on tree_id_fk, and +the other on tree_anc_id_fk,tree_dist, so you don't need to worry about that +table.

      +

      Obviously, the usage of the tree affects how many indices you want, this section +is simply to provide some general guidelines for the indices required.

      +

      Because the roots_only option is based solely on the main table and not the +tree, if you are using roots_only (calling children with id => 0 automatically +turns on the roots_only option), you want to make sure you have an index on the +root column. If you also use the max_depth depth option, add the depth column to +this index.

      +

      Keep in mind that you may need to mix other columns in here if you are using a +condition with children(). This also applies when using the sort_col and +sort_order parameters - basically you need to figure out what your indices +are, and then add in the root column and, if using max_depth, the depth column.

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Tree.pm,v 1.29 2005/05/31 06:26:32 brewt Exp $

      + + + + diff --git a/site/glist/templates/help/GT/SQL/Tree/Rebuild.html b/site/glist/templates/help/GT/SQL/Tree/Rebuild.html new file mode 100644 index 0000000..70fd819 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Tree/Rebuild.html @@ -0,0 +1,422 @@ + + + +GT::SQL::Tree::Rebuild - Helps to turn a table into one usable by GT::SQL::Tree. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::SQL::Tree::Rebuild - Helps to turn a table into one usable by GT::SQL::Tree.

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::SQL::Tree;
      +    use GT::SQL::Tree::Rebuild;
      +
      +    my $rebuild = GT::SQL::Tree::Rebuild->new(
      +        table => $DB->table('MyTable'),
      +        missing_root => \&root_code,
      +        missing_father => \&father_code,
      +        missing_depth => \&depth_code,
      +        order_by => 'column_name'
      +    );
      +
      +    $DB->editor('MyTable')->add_tree(root => $root_col, father => $father_col, depth => $depth_col, rebuild => $rebuild);
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::SQL::Tree::Rebuild is designed to go hand-in-hand with GT::SQL::Tree and +aids in turning an existing table into one with the neccessary root, father and +depth columns needed by GT::SQL::Tree.

      +

      The main purpose is to do a one-shot conversion of a table to make it compatible +with GT::SQL::Tree.

      +

      +

      +

      new - Create a Rebuild object

      +

      There is only one method that is called - new. You pass the arguments needed +and get back a GT::SQL::Tree::Rebuild object. This object should then be passed +into GT::SQL::Tree->create (typically via $editor->add_tree())

      +

      new() takes a hash with up to 4 argument pairs: ``table'' (required), and one or +more of ``missing_root'', ``missing_father'', or ``missing_depth''. The values are +explained below.

      +
      +
      table
      +
      +
      +Required. You specify the table object for the table to rebuild. For example, if +you are going to add a tree to the ``Category'' table, you provide the ``Category'' +table object here. +
      +

      +
      cols
      +
      +
      +By default, an entire row will be returned. To speed up the process and lower +the memory usage, you can use the cols option, which specifies the columns to +select for $row. It is recommended that you only select columns that you need as +doing so will definately save time and memory. +
      +

      +
      missing_father, missing_root, missing_depth
      +
      +
      +Each of these arguments takes a code reference as its value. The arguments to +the code references are as follows: +
      +
      +
      $row
      +
      +
      +The first argument is a hash reference of the row being examined. Your job, in +the code reference, is to examine $row and determine the missing value, +depending on which code reference is being called. missing_root needs to return +the root_id for this row; missing_father needs to return the father_id, and the +missing_depth code reference should return the depth for the row. +
      +

      +
      $table
      +
      +
      +The second argument passed to the code references is the same table object that +you pass into new(), which you can select from if neccessary. +
      +

      +
      missing_father
      +
      +
      +The missing_father code reference is called first - before missing_root +and missing_depth. The code reference is called as described above and should +return the ID of the father of the row passed in. A false return (0 or undef) is +interpreted as meaning that this is a root and therefore has no father. +
      +

      +
      missing_root
      +
      +
      +missing_root has to return the root of the row passed in. This is called +after missing_father, so the $row will contain whatever you returned in +missing_father in the father ID column. Of course, this only applies if using +both missing_root and missing_father. +
      +

      +
      missing_depth
      +
      +
      +missing_depth has to return the depth of the row passed in. This is called +last, so if you are also using missing_father and/or missing_root, you +will have whatever was returned by those code refs available in the $row. +
      +

      +
      order_by
      +
      +
      +The query done to retrieve records can be sorted using the order_by option. +It should be anything valid for ``ORDER BY _____''. Often it can be useful to have +your results returned in a certain order - for example: + order_by => 'depth_column ASC' +would insure that parents come before roots. Of course, this example wouldn't +work if you are using ``missing_depth'' since none of the depth values will be +set. +
      +

      +

      Once you have a GT::SQL::Tree::Rebuild object, you should pass it into +GT::SQL::Tree->create (which typically involves passing it into +$editor->add_tree(), which passed it through). Before calculating the +tree, GT::SQL::Tree will call on the rebuild object to reproduce the father, +root, and/or depth columns (whichever you specified).

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Rebuild.pm,v 1.10 2005/04/06 23:11:08 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/SQL/Types.html b/site/glist/templates/help/GT/SQL/Types.html new file mode 100644 index 0000000..6e232d4 --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Types.html @@ -0,0 +1,720 @@ + + + +GT::SQL::Driver::Types - Column types supported by GT::SQL + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::SQL::Driver::Types - Column types supported by GT::SQL

      +

      +

      +
      +

      SYNOPSIS

      +
      +    my $c = $DB->creator('new_table');
      +    $c->cols({
      +        column_name => { type => 'INT', default => 42, not_null => 1, unsigned => 1 }
      +        # ... more columns ...
      +    });
      +
      +    my $e = $DB->editor('table_name');
      +    $e->add_col(column_name2 => { type => 'CHAR', size => 10, default => 'abc' });
      +

      +

      +
      +

      DESCRIPTION

      +

      This module should not be used directly, however the documentation here +describes the different types support by GT::SQL and any caveats associated +with those types.

      +

      +

      +
      +

      ATTRIBUTES

      +

      All types are specified as a column_name => { column definition } pair, +where the column definition should contain at least a type key containing +one of the TYPES outlined below. Commonly accepted attributes are:

      +
      +
      not_null
      +
      +
      +Used to specify that a column should not be allowed to contain NULL values. +Note that for character/string data types, a 0-character string (and, for +CHAR/VARCHAR columns, strings containing only spaces), are considered +NULL values are are not permitted if the column is specified as not_null. +The value passed to not_null should be true. +
      +

      +
      default
      +
      +
      +Used to specify a default value to be used for the column when no explicit +value is provided when a row is inserted. The default value is also used for +the value in existing rows when adding a not_null column to an existing table - +in such a case, the default is required. +
      +
      +

      Also see the TEXT section regarding caveats and limitations of +using default's for TEXT types.

      +
      +

      +

      Other column attributes are supported as outlined below. In addition to +attributes mentioned in this document, various attributes are available that +influence automatically-generated forms displayed by GT::SQL::Admin - see +the GT::SQL::Creator manpage for details on these attributes.

      +

      +

      +
      +

      TYPES

      +

      +

      +

      Integer types

      +
      +
      TINYINT
      +
      +
      +The TINYINT type specifies an 8-bit integer able to handle values from -128 +to 127. Some databases will allow larger values due to not supporting an +appropriate data type. The unsigned column attribute may turn this into +an unsigned value supporting values from 0 to 255; due to this type being +implemented as a larger integer type in some databases (which, incidentally, +coincide with the databases not supporting an unsigned 8-bit TINYINT) using +an unsigned TINYINT type will result in a column able to store any value +from 0-255, unlike most of the larger integer types below. +
      +

      +
      SMALLINT
      +
      +
      +The SMALLINT type specifies a 16-bit integer able to handle values from +-32768 to 32767. The unsigned column attribute may turn this into an +unsigned value supporting values from 0 to 65535, however this is not +guaranteed. If you need to store values in the 32768-65535 range, a larger +type is recommended. +
      +

      +
      MEDIUMINT
      +
      +
      +The MEDIUMINT type (only natively supported by MySQL) specifies a 24-bit +integer type able to hold values from -8388608 to 8388607. If the unsigned +column attribute is specified, this allows values from 0 to 16777215. Due to +this being supported with the unsigned attribute, or implemented as a larger +data type, an unsigned MEDIUMINT will always supported values up to +16777215. +
      +

      +
      INT, INTEGER
      +
      +
      +The INT type specifies a 32-bit integer able to hold values from -2147483648 +to 2147483647. If the unsigned column attribute is specified, the column +may support values from 0 to 4294967295, however this is not guaranteed. +If values larger than 2147483647 are needed, using the BIGINT type below is +recommended. INTEGER is an alias for INT. +
      +

      +
      BIGINT
      +
      +
      +The largest integral type, BIGINT specifies a 64-bit integer value able to +hold values from -9223372036854775808 to 9223372036854775807. If specified as +unsigned, the column may support values from 0 to 18446744073709551616, +but this is not guaranteed. If larger values are needed, use the DECIMAL +type with a scale value of 0. +
      +

      +
      back
      +
      +
      +

      +

      +

      Float-point types

      +
      +
      REAL, FLOAT
      +
      +
      +The REAL type specifies a 32-bit floating-point (i.e. fractional) number, +accurate to 23 binary digits (which works out to approximately 6 decimal +digits). The values may be signed, and can range from at least as small as +10^-37 to at least as large as 10^37. For more precise values, the DOUBLE +type is recommended. For exact precision (i.e. for monetary values), the +(often slower) DECIMAL type is recommended. FLOAT is an alias for +REAL. +
      +

      +
      DOUBLE
      +
      +
      +The DOUBLE type specifies a 64-bit floating-point (i.e. fractional) number, +accurate to 52 binary digits (approximately 15 decimal digits). The values +may be signed, and can range from at least as small as 10^-307 to at least as +large as 10^308 (except under Oracle - see below). For exact precision (i.e. +for monetary values), the (often slower) DECIMAL type is recommended. +
      +
      +

      Take note that Oracle doesn't properly support the full range supported by +other databases' DOUBLE types - the smallest number supported (assuming +precision to digits) is 10^-113 - specifically, the number of digits after the +decimal place may not exceed 128 - so 1.2345678901e-117 is acceptable, while +1.23456789012e-117 is not. The larger number Oracle supports is just less than +1e+126 (i.e. 9.999...e+125), as opposed to other databases' 1e+307. If you +need to store numbers larger or smaller than this amount, you'll have to find +some other way to store your numbers (i.e. Math::BigFloat with a VARCHAR).

      +
      +

      +

      +

      +

      Aribtrary precision numbers

      +
      +
      DECIMAL
      +
      +
      +The DECIMAL type is provided to support numbers of arbitrary precision. It +requires two attributes, scale and precision, where scale specifies +the number of decimal places, and precision specifies the number of overall +digits. For example, 123.45 has a precision of 5, and a scale of 2. +42 has a precision or 2, and a scale of 0. scale must be less than +precision, and precision must not exceed 38. Also, although the value +stored and retrieved is completely accurate within it's given precision and +scale range, the accuracy available for comparisons (i.e. column = number) is +only reliably accurate to approximately the same level as DOUBLE's - that is, +about 15 digits. +
      +

      +

      +

      +

      Character types

      +
      +
      CHAR
      +
      +
      +The CHAR type is used to specify a string of characters from 1 to 255 +characters long. It takes a size attribute which must be 255 or less, and +specifies the size of the column values - if not specified, 255 will be used. +This implementation's CHAR type, for historic reasons, will not pad +inserted values with spaces, but may trim trailing spaces when retrieving +and/or comparing values. Note that this is not SQL compliant CHAR +behaviour - SQL-compliant CHAR's are padded with spaces up to their size. +
      +
      +

      What this ends up meaning is that for everything except MySQL, CHAR columns +will be mapped to VARCHAR columns. Note that even MySQL, which is the only +database for which CHAR's are not automatically mapped into VARCHAR's, +will transparently convert CHAR columns to VARCHAR columns if any +non-fixed-size datatype (anything other than a CHAR or numeric types) is +used in or added to the table. As a general rule, VARCHAR is preferred over +CHAR except when dealing with columns whose values don't vary significantly +in length and are in a table that only contains fixed-size data types +(CHAR's and numeric types). Everywhere else, use VARCHAR's, since that's +what you'll be getting anyway.

      +
      +
      +

      A binary attribute is supported, which may indicates that comparisons +with this field should be case-sensitive. Note that this only works on +databases that actually have a case-sensitive CHAR field - currently, only +MySQL.

      +
      +

      +
      VARCHAR
      +
      +
      +The VARCHAR type is identical to the above CHAR type except as +follows. Unlike a CHAR, a VARCHAR column does not take up size bytes +of storage space - typically the storage space is only slightly larger +(typically 1 byte) than the size of the value stored. As such, VARCHAR's +are almost always preferred over columns, except for nearly-constant sized +data, or tables with all fixed-width data types (CHAR's, INT's, and +non-DECIMAL numeric types). VARCHAR columns will not be padded with +whitespace up to size, however trailing whitespace may be trimmed from +values. +
      +
      +

      As with CHAR, the binary attribute may make the VARCHAR values +case-sensitive for the matching purposes.

      +
      +

      +
      TEXT
      +
      +
      +The TEXT type is similar to VARCHAR types, except that they are always +case-insensitive for matching/equality, and can contain longer values. The +TEXT type takes a size attribute which contains the length required - if +not provided, a value of approximately 2 billion is used. Note that the +maximum size of the column will usually be larger than the value you specify to +size - it simply indicates to the driver to use a field capable of at least +the size specified. The values of TEXT fields are case-insensitive in terms +of matches and equality. The maximum size value, and the default, is +approximately 2 billion. +
      +
      +

      Certain aliases are provided with implicit size defaults - TINYTEXT, +SMALLTEXT, MEDIUMTEXT, and LONGTEXT, which are equivelant to TEXT +with size values of 255, 65535, 16777215, and 2147483647, respectively.

      +
      +
      +

      Depending on the size value, certain databases _may_ use different +underlying types. MySQL, for example, uses the smallest possible type between +its native TINYTEXT, TEXT, MEDIUMTEXT, and LONGTEXT types. As +such, it is recommended that you use a sufficiently large size value unless +absolutely sure that you will never need a larger value.

      +
      +
      +

      Also note that TEXT types do not support normal equality operations - in +fact, the only portable things that can be done with TEXT columns is IS +NULL tests (in GT::SQL this means ``='' undef) and LIKE comparisons - but, +for portability with all supported databases, the argument of a LIKE may not +exceed 4000 characters.

      +
      +
      +

      Also note that the default value will be ignored by MySQL, which does not +support having default values on TEXT columns. Everything else, however, +will properly support this, and the default will still be used when inserting +with GT::SQL even when using MySQL. Also note that the default value of +TEXT types must not exceed 3998 characters, due to limits imposed by some +databases. Longer indexes may work in some cases, but are not guaranteed - for +example, a table resync on MSSQL will not work.

      +
      +

      +
      ENUM
      +
      +
      +The ENUM type is a MySQL-only type that supports certain fixed string +values. On non-MySQL databases, it is simply mapped to a VARCHAR column. +It requires a values option which should have a value of an array reference +of string values that the ENUM should permit. The ENUM type is generally +discouraged in favour of a CHAR, VARCHAR, or an +integral type column, all of which provide more flexibility +(i.e. if you want to add a new possible value) and are not a single +database-specific type. +
      +

      +

      +

      +

      Date/time types

      +

      All of the date/time types support by MySQL will be handled by GT::SQL, for +compatibility reasons. However, all types other than DATE and DATETIME +should be considered deprecated as cross-database compatibility is not possible +using these types. In particular, TIMESTAMP will work exactly like a +DATETIME on every non-MySQL database; TIME and DATE will work in +Postgres just like they do in MySQL; under everything else, TIME won't work +at all, and DATE will work like DATETIME.

      +

      GT::SQL users are urged to at least consider using an INT column, designed to +contain Perl's time() value, in lieu of any of the Date/time types as it avoids +many problems typically associated with storing local times - such as time zone +issues and non-local databases. That said, if you are certain you want a +Date/time type, a DATETIME is preferred as it will work (almost) the same +everywhere.

      +
      +
      DATETIME
      +
      +
      +A date field, which stores values in YYYY-MM-DD HH:MM:SS format (where +'HH' is a 24-hour hour). Inserted values may omit the seconds +(YYYY-MM-DD HH:MM), or time (YYYY-MM-DD) portions of the value. Omitted +values will default to 0. +
      +
      +

      Note that DATETIME values returned from a database may include +fractional-second precision values such as 2004-01-01 12:00:07.123. +Currently MSSQL and Postgres exhibit this behaviour. MSSQL's DATETIME type +always includes exactly three decimal digits, while Postgres' TIMESTAMP type, +used for GT::SQL DATETIME's, stores times with 6 decimal-digit precision. +Unlike MSSQL, however, Postgres will only display decimal digits if a +significant decimal value has been stored in the database. This happens with +the time_check option, below, and when an explicit fractional second value +has been inserted into the database.

      +
      +
      +

      A time_check attribute may be passed with a true value; if set, any update +to the row that doesn't explicitly set the column will have the column updated +to the database's current local time. Due to issues with times and/or +timezones, this option should be considered deprecated and discouraged - it is +recommended instead that you update the value yourself using a value that +your script thinks is local time (or, better yet, use an INT column with +unix time values (i.e. time() in Perl), which are timezone-independent to begin +with), rather than trying to depend on a database having the same time and time +zone as your script.

      +
      +

      +
      DATE
      +
      +
      +Just like DATETIME, except (under MySQL and Postgres) it only stores and +returns the YYYY-MM-DD portion of the value. Note that when using this +type, care must be taken to extract only the desired portion of the output as +databases other than MySQL and Postgres map this to a DATETIME above, which +returns 'YYYY-MM-DD HH:MM:SS' values (with a possible fractional seconds value, +in the case of MSSQL/Postgres). Using a DATETIME or INT field is +generally preferred, but this type may be slightly more effecient and take +slightly less space (4 bytes instead of 8 bytes) on MySQL and Postgres +databases. +
      +
      +

      Like DATETIME, this handles a time_check field, with the same caveats +described in the the DATETIME time_check description.

      +
      +

      +

      The alternate, deprecated date/time types supported are listed in the +Deprecated types section below.

      +

      +

      +

      Deprecated types

      +
      +
      BLOB
      +
      +
      +Limited BLOB support (TINYBLOB, BLOB, MEDIUMBLOB, and LONGBLOB) +existed in older versions of GT::SQL, however the support, where it existed at +all, was partial and incomplete. Additionally, only certain drivers (MySQL and +Oracle) supported BLOB types at all. As such, the limited BLOB support +present in old GT::SQL versions is still supported under MySQL and Oracle, but +any new development should avoid them. If you really need to store binary +data, it is strongly recommended that you use files, and simply store +filenames in the database. +
      +

      +
      TIMESTAMP
      +
      +
      +This extremely odd MySQL data type, depending on the version of MySQL, stores +times in either the format described in DATETIME (MySQL 4.1+) or an +extremely MySQL-specific YYYYMMDDhhmmss format. Another MySQL-specific of +this data type is that the first - and ONLY the first - TIMESTAMP column in +a row will be automatically updated to the current local timezone-dependent +date and time. Use a DATETIME (possibly with the time_check option) +instead. +
      +

      +
      TIME
      +
      +
      +A MySQL and Postgres-specific type that stores only the time-of-day in +HH:MM:SS format. Deprecated due to non-portability and incompatibility on +other databases. If you really want to store just the time of day, either use +an INT to store the minutes or seconds since midnight, or use a CHAR +which you update with the HH:MM:SS value. Causes a fatal error on databases +which don't have an appropriate native type. +
      +

      +
      YEAR
      +
      +
      +A particularly useless MySQL-specific data type that stores only the year +portion of a date. Use a SMALLINT instead. Causes a fatal error on +anything other than MySQL. +
      +

      +

      +

      +
      +

      SEE ALSO

      +

      the GT::SQL manpage

      +

      the GT::SQL::Creator manpage

      +

      +

      +
      +

      MAINTAINER

      +

      Jason Rhinelander

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Types.pm,v 1.2 2004/09/07 20:56:59 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/SQL/Upgrade.html b/site/glist/templates/help/GT/SQL/Upgrade.html new file mode 100644 index 0000000..902fd6c --- /dev/null +++ b/site/glist/templates/help/GT/SQL/Upgrade.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/SQL/Upgrade.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/Session/File.html b/site/glist/templates/help/GT/Session/File.html new file mode 100644 index 0000000..e7c2428 --- /dev/null +++ b/site/glist/templates/help/GT/Session/File.html @@ -0,0 +1,306 @@ + + + +GT::Session::File - A session management module, with simple data storage/retrieval. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Session::File - A session management module, with simple data storage/retrieval.

      +

      +

      +
      +

      SYNOPSIS

      +

      Create a session: + my $session = new GT::Session::File; + my $id = $session->id();

      +

      Save data with the session: + $session->data (``Save this information!'');

      +

      Load a session. + my $session = new GT::Session::File ( $id ) or die ``Can't load session: '$id'.''

      +

      Set session directory. + my $session = new GT::Session::File ( directory => '/path/to/sessions', id => $id );

      +

      Delete a session + $session->delete();

      +

      Cleanup old sessions, takes argument of number of seconds old. + $session->cleanup ( 5000 );

      +

      +

      +
      +

      TODO

      +

      * Integrate SQL interface into flatfile interface.

      + + + + diff --git a/site/glist/templates/help/GT/Session/SQL.html b/site/glist/templates/help/GT/Session/SQL.html new file mode 100644 index 0000000..a30bd13 --- /dev/null +++ b/site/glist/templates/help/GT/Session/SQL.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/Session/SQL.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/Session/TempTable.html b/site/glist/templates/help/GT/Session/TempTable.html new file mode 100644 index 0000000..6425e1a --- /dev/null +++ b/site/glist/templates/help/GT/Session/TempTable.html @@ -0,0 +1,311 @@ + + + +GT::Session::TempTable - A session management module, subclassing GT::Session::SQL providing temp table support + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Session::TempTable - A session management module, subclassing GT::Session::SQL providing temp table support

      +

      +

      +
      +

      SYNOPSIS

      +

      Create a session: + my $session = new GT::Session::TempTable({ + db => GT::SQL->new( '/path/to/defs' ), + def_path => '/path/to/defs', + create_session => \&create_table_sub + });

      +

      Create temp table controller table. (do once before using this module) + $session->initial_create();

      +

      Create a new temp table: + my ( $GT_SQL_Table_ref, $tmp_id ) = $session->new_set();

      +

      Get the GT::SQL::Table ref to a previous table: + my $GT_SQL_Table_ref = $session->get_set( $tmp_id );

      +

      List all the sets for current session: + my $href = $session->list_sets();

      +

      Save data with the session: + $session->data (``Save this information!'');

      +

      Load a session. + my $session = new GT::Session::TempTable ( $id ) or die ``Can't load session: '$id'.''

      +

      Delete a session: + $session->delete();

      +

      Delete a table set: + $session->delete_set( $tmp_id );

      +

      Cleanup old sessions, takes argument of number of seconds old. + $session->cleanup ( 5000 );

      + + + + diff --git a/site/glist/templates/help/GT/Socket.html b/site/glist/templates/help/GT/Socket.html new file mode 100644 index 0000000..6f9c707 --- /dev/null +++ b/site/glist/templates/help/GT/Socket.html @@ -0,0 +1,621 @@ + + + +GT::Socket - A simple internet socket handling interface + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Socket - A simple internet socket handling interface

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::Socket;
      +
      +    my $sock = GT::Socket->open({
      +        host => 'www.gossamer-threads.com',
      +        port => 80
      +    });
      +
      +    $sock->write("GET / HTTP/1.0\n\n");
      +
      +    print "REQUEST RETURNED:\n\n", $sock->gulpread(-1);
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::Socket provides a simple interface for tcp client/server socket services.

      +

      +

      +

      Method List

      +

      Object Creation

      +
      +    open()        Creates a new client socket
      +    server()      Creates a new server socket
      +

      Reading and Writing

      +
      +    write()       Sends all or up to max_up bytes of data to remote
      +    read()        Receives an amount or max_down bytes of data from remote
      +    gulpread()    Gets all or up to max_down bytes of data from remote
      +

      Socket Administration

      +
      +    close()       Closes the socket
      +    EOF()         Returns open/closed status of socket
      +    autoflush()   Sets the socket so that no data is buffered
      +    vec()         Sets bits in a bitmask for select calls
      +    pending()     Returns true if data/clients awaiting
      +    fh()          Returns the raw socket handle
      +

      Server Handling

      +
      +    accept()      Accepts a incoming client request
      +

      +

      +

      Creating a new Client Socket

      +

      To instantiate a new Client Socket connection, the open() method must be +called.

      +
      +    my $sock = GT::Socket->open({
      +        host => 'hostname', # hostname/ip to connect to
      +        port => 1234,       # port to connect to
      +        max_down => 0,      # maximum number of bytes to download (optional)
      +        max_up => 0,        # maximum number of bytes to upload (optional)
      +        timeout => 10       # maximum time to wait for host connect (optional)
      +    });
      +

      The parameters are somewhat flexible, to connect to www.gossamer-threads.com on +port 80, any of the following calling methods can be used.

      +
      +    my $sock = GT::Socket->open({
      +        host => 'www.gossamer-threads.com', 
      +        port => 80  
      +    });
      +
      +    my $sock = GT::Socket->open(
      +        host => 'www.gossamer-threads.com', 
      +        port => 80 
      +    );
      +
      +    my $sock = GT::Socket->open('www.gossamer-threads.com', 80);
      +
      +    my $sock = GT::Socket->open('www.gossamer-threads.com:80');
      +

      Note that as port 80 is the HTTP port, and port gets tested and handled with +the getservbyname function, the following can be done:

      +
      +    # 'http' here but can be 'pop3', 'telnet', etc. depending on service wanted
      +    my $sock = GT::Socket->open('www.gossamer-threads.com', 'http');
      +

      Note that if the value passed to open() is a hash ref, with a host and port, a +handful of other options may be set.

      +

      +

      +

      Limiting maximum amount of data downloaded

      +

      This affects the $sock->read() and the $sock->gulpread() methods.

      +

      The option 'max_down' can be used to put a cap on the number of bytes recieved +through the socket.

      +

      For example to limit the number of bytes downloaded to 2k, set max_down to 2048

      +
      +    my $sock = GT::Socket->open(
      +        host => 'www.gossamer-threads.com',
      +        port => 80,
      +        max_down => 2048
      +    );
      +

      WARNING, once the download maximum has been reached, the socket is closed. Then +no more information can be uploaded to the remote host.

      +

      +

      +

      Limiting maximum amount of data uploaded

      +

      The option 'max_up' is used to limit the number of bytes that can be sent to +the remote host.

      +

      After the maximum number of bytes is hit, the object will no longer carry out +$sock->write() requests.

      +

      This does not affect the number of bytes that can be downloaded. Until max_down +is hit or the remote host finishes the transmission, the socket will keep +listening.

      +

      In the following example. The maximum number of bytes for both download and +upload have been set to 2K.

      +

      Keep in mind, with this example, if the maximum download limit is reached +before the maximum upload, the socket will be closed so the remote server will +stop responding to $sock->write() as well!

      +
      +    my $sock = GT::Socket->open(
      +        host => 'www.gossamer-threads.com',
      +        port => 80,
      +        max_down => 2048,
      +        max_up => 2048
      +    );
      +

      +

      +

      Limiting time taken to connect to a host

      +

      When the module tries to connect to a host, if the host is not running or +simply not present, it may take over 30 seconds for the connect call to give +up.

      +

      The 'timout' option allows the forcing the waiting period to be a certain +number of seconds. By default, the value is set to 10 seconds.

      +

      Since this uses alarm, it will not function on Win32 machines.

      +

      With the following example, the module will spend a maximum of 3 seconds trying +to connect to www.gossamer-threads.com.

      +
      +    my $sock = GT::Socket->open( 
      +        host => 'www.gossamer-threads.com', 
      +        port => 80,
      +        timeout => 3
      +    );
      +

      +

      +

      Methods

      +

      The following methods are available to the Client object

      +

      +

      +

      autoflush ( flag BOOLEAN )

      +
      +    $sock->autoflush(1) # turn on flushing
      +    $sock->autoflush(0) # turn off flushing
      +

      Turns off buffering for the socket. By default, the socket is +autoflushed/buffering turned off.

      +

      This prevents peculiar errors like stalling when trying to communicate with +http servers.

      +

      +

      +

      close

      +

      Closes the socket if open.

      +

      +

      +

      EOF

      +

      Returns true of the socket is closed.

      +

      +

      +

      fh

      +

      Returns the filehandle.

      +

      The return value is file glob, because of this, the upload/download limits +cannot be enforced and the accounting can fall to bits of both the object and +the file glob are being used simultaneously.

      +

      +

      +

      gulpread ( tics INTEGER )

      +

      Attempts to read all the data it can into a buffer and return. If max_down is +non zero, it will read till the remote closes or the limit has been reached and +returns.

      +

      Tics is a non-zero value that will determine how long the function will run for +or wait:

      +
      +    $tics     Action
      +    ----------------------------------------
      +    >0        Wait $tics seconds till returning with results
      +    0         Don't wait, simply get what's there and return
      +    <0        Block, wait until all the data (up to max_down) has been received
      +

      +

      +

      pending ( tics INTEGER )

      +

      Returns true if socket has data pending to be received. Usually this would be +followed with a call to $sock->gulpread() or $sock->read()

      +
      +    $tics     Action
      +    ----------------------------------------
      +    >0        Wait $tics seconds till returning with results
      +    0         Don't wait, simply get what's there and return
      +    <0        Block, wait until all the data (up to max_down) has been received
      +

      +

      +

      read ( number_bytes INTEGER )

      +

      Reads a max of number_bytes from the socket or up to max_down and returns the +result. This is nonblocking so it is possible to get no data or less than the +requested amount.

      +

      +

      +

      vec ( [ bits SCALAR ] )

      +

      Sets the bits appropriate for the object's socket handle. The returned value +can be used in select(RBITS,WBITS,EBITS,TIMEOUT) function calls.

      +

      To test a series of socket handles, vec accepts an already set bit list from +another vec call.

      +
      +    $bits = $sock1->vec();
      +    $bits = $sock2->vec($bits);
      +    $bits = $sock3->vec($bits);
      +

      And $bits can now be used to test on all three handles.

      +

      +

      +

      write ( buffer SCALAR )

      +

      Takes the buffer and send it into the socket or up to the max_up limit.

      +

      Returns the number of bytes sent.

      +

      +

      +

      Creating a new Server Socket

      +

      Creating a server socket is almost identical to creating a client socket except +no hostname is specified. +

      +
      +
      +    my $server = GT::Socket->server({
      +        port => 1234,  # port to host services
      +        max_down => 0, # maximum number of bytes to download (optional)
      +        max_up => 0,   # maximum number of bytes to upload (optional)
      +        timeout => 10  # maximum time to wait for host connect (optional)
      +    });
      +

      The only option that affects the server directly is the port. The optional +values, max_down, max_up, and timeout are passed on to the child socket when +the server accepts a new connection.

      +

      +

      +

      Methods

      +

      The following methods are available to the Client object

      +

      +

      +

      accept

      +

      Accepts an incoming connection and returns a GT::Socket client object for +further interations with the client.

      +

      +

      +

      fh

      +

      Returns the filehandle.

      +

      +

      +

      pending ( tics INTEGER )

      +

      Returns true if server has awaiting connections. Usually this would be followed +with a call to $server->accept();

      +
      +    $tics     Action
      +    ----------------------------------------
      +    >0        Wait $tics seconds till returning with results
      +    0         Don't wait, simply get what's there and return
      +    <0        Block, wait until all the data (up to max_down) has been received
      +

      +

      +

      vec ( [ bits SCALAR ] )

      +

      Sets the bits appropriate for the object's socket handle. The returned value +can be used in select(RBITS,WBITS,EBITS,TIMEOUT) function calls.

      +

      To test a series of socket handles, vec accepts an already set bit list from +another vec call.

      +
      +    $bits = $sock1->vec();
      +    $bits = $sock2->vec($bits);
      +    $bits = $sock3->vec($bits);
      +

      And $bits can now be used to test on all three handles.

      +

      +

      +
      +

      EXAMPLES

      +

      +

      +

      Server

      +
      +    use GT::Socket;
      +
      +    my $server = GT::Socket->server({
      +        port => 7890
      +    });
      +
      +    while (1) {
      +        if ($server->pending(-1)) {
      +            print "Accepting a connection\n";
      +            my $sock = $server->accept();
      +            $sock->write("The time is: " . localtime() . "\n");
      +        }
      +    }
      +

      +

      +

      Client for Server

      +
      +    use GT::Socket;
      +
      +    my $client = GT::Socket->open("localhost:7890");
      +    print "Server Said: ", $client->gulpread(-1);
      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2000 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Socket.pm,v 1.43 2004/08/23 20:07:44 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/Socket/Client.html b/site/glist/templates/help/GT/Socket/Client.html new file mode 100644 index 0000000..a7696b0 --- /dev/null +++ b/site/glist/templates/help/GT/Socket/Client.html @@ -0,0 +1,528 @@ + + + +GT::Socket::Client - Socket module designed for TCP clients + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Socket::Client - Socket module designed for TCP clients

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::Socket::Client qw/:crlf/;
      +
      +    my $socket = GT::Socket::Client->open(
      +        host => "gossamer-threads.com",
      +        port => "shell", # AKA port 514
      +        timeout => 10
      +    ) or die GT::Socket::Client->error;
      +
      +    # $socket is now a socket connected to the host. Use
      +    # it as you would use any socket.
      +    $sock->readline(my $line);
      +    print "Read this line from the socket: $line";
      +    print $sock "That line" . CRLF;
      +
      +    $sock->readblock(my $block, 4096);
      +    print "Read 4KB from the socket: $block";
      +    print $sock "QUIT" . CRLF;
      +
      +    $sock->readall(my $all);
      +    print "Everything else from the socket: $all";
      +    print $sock "Something else" . CRLF;
      +

      +

      +
      +

      DESCRIPTION

      +

      This module is a basic socket module that is designed to only handle basic +socket connection and simple read capabilities. Anything else that you want to +do with the socket is entirely up to you - this doesn't try to support +superfluous options that only a few connections will ever use, or options that +should be done in the code using this module instead of the module itself. See +the GT::WWW::http and GT::WWW::https modules for a good working example.

      +

      By default, GT::Socket::Client exports nothing, however it can export the LF, +CR, CRLF, $LF, $CR, and $CRLF constants, individually, or together via the +':crlf' export tag.

      +

      +

      +
      +

      METHODS

      +

      +

      +

      open

      +

      Takes a hash (not hash reference) of socket options, as follows:

      +
      +
      host
      +
      +
      +[REQUIRED] The name or IP of the host to connect to. +
      +

      +
      port
      +
      +
      +[REQUIRED] The numeric value (25) or service name (``smtp'') of the port to +connect to. +
      +

      +
      ssl
      +
      +
      +[OPTIONAL] If this option is provided, the connection will use SSL. Note that +this requires the Net::SSLeay module. +
      +

      +
      timeout
      +
      +
      +[OPTIONAL] A connection timeout period, in integral seconds. Note that this is +only supported on systems that support the alarm() function; on other systems +(such as Windows), this argument has no effect. +
      +

      +
      non_blocking
      +
      +
      +[OPTIONAL] Before returning it to you, the connected socket will be set up as +non-blocking if this option is enabled. Note that this option DOES NOT WORK +with the ssl option, due to the Net::SSLeay interface. +
      +

      +
      autoflush
      +
      +
      +[OPTIONAL] Before returning to you, the connected socket will be made non- +buffering. If you want your socket to be buffered, pass in autoflush with a +false value. +
      +

      +
      ssl
      +
      +
      +[OPTIONAL] GT::Socket::Client has the ability to establish an SSL connection to +a server for protocols such as HTTPS, SMTPS, POP3S, IMAPS, etc. Note that it +currently has a limitation of not being able to change to or from an SSL +connection once the connection is established, for protocols like FTPS. +
      +

      +
      debug
      +
      +
      +[OPTIONAL] If debugging is enabled, internal warnings (such as invalid port, +unresolvable host, connection failure, etc.) will be warn()ed. This does not +affect the error() method, which will always be set to the error message when +a problem occurs. Provide a true value if you want the warn()s to appear. +
      +

      +

      +

      +

      readline

      +

      This method reads a single line from the socket. It takes one argument, which +must be a scalar which will be set to the line read. See the eol() method, +which allows you to specify an EOL character other than ``\012''. Note that on a +blocking socket, this will block until it can read a full line (or the server +closes the connection). On a non-blocking socket, the amount of time it will +wait for input is dependent on the value of the read_wait() method.

      +

      1 is returned on success, undef on failure.

      +

      +

      +

      readblock

      +

      This method attempts to read a certain number of bytes from the server. This +takes two arguments: like readline(), the first argument is a scalar that will +be set to the data read. The second argument is the amount of data that may be +read. Note that on a blocking socket, this will block until the required +amount of data is read, or the socket is closed. On a non-blocking socket, this +will return once the requested amount of data is read, the socket closes, or +there is no input for read_wait seconds (See read_wait).

      +

      Note that a block size of -1 makes the socket read until the connection is +closed, in the case of blocking sockets, or until the read_wait() is hit.

      +

      The number of bytes read is returned on success, undef on failure.

      +

      +

      +

      readall

      +

      A synonym for $obj->readblock($_[0], -1) - in other words, it reads all +available data (waiting for up to read_wait seconds, if non-blocking).

      +

      +

      +

      readalluntil

      +

      A useful function for non-blocking sockets (completely useless for blocking +sockets, on which it simply becomes a readall call). Basically, this works +like readall(), above, but it will terminate immediately if it encounters a +pattern that you provide on the end of the data read. Note that this does NOT +work as a delimiter, but is useful for protocols such as POP3 when you want to +read as much as you can, but know what should be at the end of what you read. +The sole advantage of this is that it allows you to avoid the read_wait timeout +that would otherwise be required at the end of a data stream.

      +

      It takes two arguments - the first is a string or array reference of strings +containing the trailing string data. The second is a scalar that will be set +to the data read. For example, for POP3 you might use: "\n.\r\n". You can +optionally pass in a third argument, which is used during the first read - if +the result of the first read is equal to the string passed in, it's returned. +Using the POP3 example again, this might be ".\r\n" - to handle an empty +response.

      +

      +

      +

      select_time

      +

      [Non-blocking sockets only] This adjusts the number of seconds passed to +select() to poll the socket for available data. The default value is 0.05, +which should work in most situations.

      +

      +

      +

      read_wait

      +

      [Non-blocking sockets only] This method is used to set the wait time for reads. +On a local or very fast connection, this can be set to a low value (i.e. 0.1 +seconds), but on a typical slower internet connection, longer wait times for +reading are usually necessary. Hence, the default is a wait time of 5 seconds. +In effect, an attempt to read all data will end after nothing has been received +for this many seconds.

      +

      +

      +

      write

      +

      Sends data to the server. Takes the data to send. This does The Right Thing +for either non-blocking or blocking sockets.

      +

      +

      +

      eol

      +

      This method takes one or more character, and uses it for the EOL character(s) +used by readline. If called without any argument, the EOL character for the +current object is returned.

      +

      +

      +

      error

      +

      If an error (such as connection, socket, etc.) occurs, you can access it via +the error() method. This can be called as either a class or instance method, +since open() return undef instead of an object if the connection fails.

      +

      +

      +

      iaddr

      +

      Once a connection has been established, you can call this method to get the +iaddr value for the connection. This value is as returned by +Socket.pm's inet_aton function.

      +

      +

      +

      port

      +

      Once a connection has been established, this method can be used to determine +the port connected to. Note that this is not necessarily the same as the value +of the port option passed to open() - the return value of this function will +always be numeric (e.g. 25), even if a service name (e.g. "smtp") was +passed to open().

      +

      +

      +
      +

      SEE ALSO

      +

      the GT::Socket manpage - A socket module made for Links SQL.

      +

      +

      +
      +

      MAINTAINER

      +

      Jason Rhinelander

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Client.pm,v 1.15 2004/02/17 01:33:07 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/Socket/Client/SSLHandle.html b/site/glist/templates/help/GT/Socket/Client/SSLHandle.html new file mode 100644 index 0000000..4ef26f5 --- /dev/null +++ b/site/glist/templates/help/GT/Socket/Client/SSLHandle.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/Socket/Client/SSLHandle.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/Tar.html b/site/glist/templates/help/GT/Tar.html new file mode 100644 index 0000000..99bcf89 --- /dev/null +++ b/site/glist/templates/help/GT/Tar.html @@ -0,0 +1,441 @@ + + + +GT::Tar - Perl module to manipulate tar files. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Tar - Perl module to manipulate tar files.

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::Tar;
      +    my $tar = GT::Tar->open('foo.tar');
      +    $tar->add_file( '/path/to/file' );
      +    $tar->write;
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::Tar provides an OO intefrace to a tar file. It allows you to create or edit +tar files, and if you have Compress::Zlib installed, it allows you to work with +.tar.gz files as well!

      +

      +

      +

      Creating a tar file

      +

      To create a tar file, you simply call:

      +
      +    my $tar = new GT::Tar;
      +

      and then to save it:

      +
      +    $tar->write('filename.tar');
      +

      will save the tar file and any files you have added.

      +

      +

      +

      Opening an existing tar file

      +

      To open a tar file you call:

      +
      +    my $tar = GT::Tar->open('/path/to/file.tar')
      +        or die "Can't open: $GT::Tar::error";
      +

      Note: the tar object keeps an open filehandle to the file, so if you are on +windows, you may not be able to manipulate it until you call $tar->close_tar, or +the tar object goes out of scope.

      +

      +

      +

      Untarring a tar file

      +

      To untar a tar file, you can simply call:

      +
      +    $tar->untar( \&code_ref );
      +

      or as a class method

      +
      +    GT::Tar->untar('/path/to/tar.tar', \&code_ref );
      +

      The code ref is optional. If provided, you will get passed in the a +GT::Tar::Part object before the file is extracted. This lets you change the +path, or alter any attributes of the file before it is saved to disk.

      +

      Alternatively, instead of a code reference you may pass an extraction path - if +passed, all files will be extracted relative to that path.

      +

      +

      +

      Adding files to a tar file

      +

      To add a file:

      +
      +    $tar->add_file( '/path/to/file' );
      +

      Note, if you add a directory, the tar module will recurse and add all files in +that directory.

      +

      To add a file that isn't saved:

      +
      +    $tar->add_data( name => 'Filename', body => 'File body' );
      +

      You can pass in either a scalar for the body, or an opened file handle.

      +

      +

      +

      Getting a list of files in a tar

      +

      To get a list of files in a tar:

      +
      +    my $files = $tar->files;
      +

      This returns an array ref of GT::Tar::Part objects. See below for how to access +information from a part.

      +

      Note: if you change a part, it will update the tar file if you save it.

      +

      +

      +

      Getting an individual file from a tar

      +

      If you know the name of the file you want:

      +
      +    my $file = $tar->get_file('Filename');
      +

      will return a single GT::Tar::Part object.

      +

      +

      +

      Removing a file from a tar

      +

      To remove a file, you need to know the name of it:

      +
      +    $tar->remove_file('Filename');
      +    $tar->write;
      +

      and you need to save it before the change will take affect.

      +

      +

      +

      GT::Tar::Part

      +

      Each file is a separate part object. The part object has the following +attributes:

      +
      +    name    file name
      +    mode    file permissions
      +    uid     user id
      +    gid     group id
      +    size    file size
      +    mtime   last modified time
      +    type    file type
      +    body    file body
      +

      You can access or set any of these attributes by just using the attribute name +as the method (as it inherits from the GT::Base manpage).

      +

      You can also call:

      +
      +    $file->write;
      +

      or:

      +
      +    $file->write("/extraction/path")
      +

      and the file will be created with the given attributes. Basically untar just +foreach's through each of the objects and calls write() on it.

      +

      +

      +
      +

      EXAMPLES

      +

      To create a new tar and add two directories to it, and save it in +'/tmp/foo.tar';

      +
      +    my $tar = new GT::Tar;
      +    $tar->add_file( '/home/httpd/html' );
      +    $tar->add_file( '/home/backup' );
      +    $tar->write('/tmp/foo.tar');
      +

      To open an existing tar file and save all the .pl files in /home/alex.

      +
      +    my $tar = GT::Tar->open('files.tar');
      +    my $files = $tar->files;
      +    foreach my $file (@$files) {
      +        my $name = $file->name;
      +        if ($name =~ m,[^/]*\.pl$,) {
      +            $file->write("/home/alex");
      +        }
      +    }
      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Tar.pm,v 1.54 2005/03/09 01:26:17 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/TempFile.html b/site/glist/templates/help/GT/TempFile.html new file mode 100644 index 0000000..eebcd0b --- /dev/null +++ b/site/glist/templates/help/GT/TempFile.html @@ -0,0 +1,338 @@ + + + +GT::TempFile - implements a very simple temp file. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::TempFile - implements a very simple temp file.

      +

      +

      +
      +

      SYNOPSIS

      +
      +    my $file = new GT::TempFile;
      +    open (FILE, "> $file");
      +    print FILE "somedata";
      +    close FILE;
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::TempFile implements a very simple temp file system that will remove +itself once the variable goes out of scope.

      +

      When you call new, it creates a random file name and looks for a +tmp directory. What you get back is an object that when dereferenced +is the file name. You can also pass in a temp dir to use:

      +
      +    my $file = new GT::Tempfile '/path/to/tmpfiles';
      +

      Other option you may use are: + my $file = new GT::TempFile( + destroy => 1, + prefix => '', + tmp_dir => '/tmp' + );

      +

      When the object is destroyed, it automatically unlinks the temp file +unless you specify destroy => 0.

      +

      prefix will be prepended to the start of all temp files created +and the lock directory that is created. It is used to keep programs +using the tempfile module that do not have the temp files destroyed +from clashing.

      +

      tmp_dir is the same as calling new with just one argument, it is +the directory where files will be stored.

      +

      TempFile picks a temp directory based on the following:

      +
      +    1. ENV{GT_TMPDIR}
      +    2. ~/tmp
      +    3. ENV{TMPDIR}, ENV{TEMP}, ENV{TMP}
      +    4. /usr/tmp, /var/tmp, c:/temp, /tmp, /temp, 
      +       /WWW_ROOT, c:/windows/temp, c:/winnt/temp
      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: TempFile.pm,v 1.36 2005/03/23 04:27:26 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/Template.html b/site/glist/templates/help/GT/Template.html new file mode 100644 index 0000000..77b234e --- /dev/null +++ b/site/glist/templates/help/GT/Template.html @@ -0,0 +1,550 @@ + + + +GT::Template - Gossamer Threads template parser + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Template - Gossamer Threads template parser

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::Template;
      +    my $var = GT::Template->parse('file.txt', { key => 'value' });
      +    ...
      +    print $var;
      +

      or

      +
      +    use GT::Template;
      +    GT::Template->parse_print('file.txt', { key => 'value' });
      +

      or

      +
      +    use GT::Template;
      +    GT::Template->parse_stream('file.txt', { key => 'value' });
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::Template provides a simple way (one line) to parse a template (which +can be either a file or a string) and make sophisticated replacements.

      +

      It supports simple replacements, conditionals, function calls, including other +templates, and more.

      +

      Additionally, through using pre-compiled files, subsequent parses of a template +will be very fast.

      +

      +

      +

      Template Syntax

      +

      The template syntax documentation has moved - it is now documented in +the GT::Template::Tutorial manpage.

      +

      +

      +

      parse

      +

      This option parses a template, and returns the value of the parsed template. +See Parse Options for a description of the possible parse parameters.

      +

      +

      +

      parse_print

      +

      This option parses a template, and prints it. See Parse Options for a +description of the possible parse_print parameters.

      +

      +

      +

      parse_stream

      +

      This option parses a template, and prints each part of it as the parse occurs. +It should only be used in situations where streaming content is required as it +is measurably slower than the parse_print alternative. See Parse Options +for a description of the possible parse_stream parameters.

      +

      +

      +

      Parse Options

      +

      +

      +

      Filename

      +

      The first argument to parse()/parse_print()/parse_stream() (hereafter referred +to simply as parse()) is the full or relative (to the current working +directory) path to the file to parse.

      +

      +

      +

      Variables

      +

      The second argument is a hash reference of template variables that will be +available in the parsed template (see the GT::Template::Tutorial manpage). Arbitrary +hash/array data structure access is supported (see +Advanced variables using references in the GT::Template::Tutorial manpage).

      +

      Loops are supported by providing an array reference or code reference as a +value; array reference loops are generally preferred as they enable the loop to +be used multiple times and support the <%loopvar.length%> syntax.

      +

      +

      +

      Options

      +

      The third argument (which is not required) takes additional options that change +the way a parse is performed. The available options (there are more, however +their use is discouraged) are as follows.

      +
        +
      • string => $template
        +
      • +Passing in string => $template will use $template as for the template +content instead of reading the file specified as the first parse() argument. +If provided, the first argument to parse() (the filename) is ignored. +

        +
      • compress => 1
        +
      • +Setting compress => 1 will compress all white space generated by the program. +This is usually acceptable for HTML, reducing page sizes by typically 10-20%, +but should not be used for non-HTML templates. The default is 0 (no +compression). This option has no effect when using parse_stream(). +

        +
      • strict => 0
        +
      • +If set to 1, attempting to use a tag that does not exist will display an +``Unknown tag 'tagname''' error. If strict is set to 0, using an unset tag will +not display anything. +

        +
      • escape => 1
        +
      • +If enabled, this option will cause all variables to be HTML escaped before +being included on a page. Enabling this option is strongly recommended. +all variables before they are printed. Tag values that should not be escaped +should be passed as scalar references (\$foo or \'<html>'). +

        This option currently defaults to 0, but may eventually change to 1 - so +passing an explicit 1 or 0 value is strongly recommended.

        +

        +
      • disable => { ... }
        +
      • +This can be used to disable certain GT::Template functionality. To disable a +particular feature, the hash reference passed to disable should contain a +feature_name with a 1 value, unless otherwise indicated. Feature names +are as follows: +
          +
        • functions
          +
        • +This can be used to disable Package::function calls, such as +<%Some::Package::function%>. Note, however, that this does _not_ +disable aliased function calls (see below). +

          +
        • function_args
          +
        • +This disables any function calls that specify arguments - for instance, +<%Some::Package::function(1)>. Note that this does _not_ disable +passing arguments to aliased function calls (see below). +

          +
        • function_restrict
          +
        • +This can be used to restrict function calls by limiting the available +functions. It takes a regular expression as an argument, which will be tested +against the fully qualified function name - any function that does not match +the regular expression will not be called. For example, to only allow +functions in 'Package::One' and 'Second::Package' to be called, you could use: +
          +    function_restrict => '^(?:Package::One|Second::Package)::\w+$'
          +

          Like the above options, this does not restrict aliased function calls.

          +

          +
        • coderefs_args
          +
        • +This can be specified to disable the calling of code reference variables with +arguments. Tags such as <%coderefname%> and +<%coderefname()%> will be allowed, but <%coderefname(1)%> +will not. +

          +
        • alias_args
          +
        • +This option can be used to disable the passing of arguments to aliased function +calls (see below). +

        +
      • pkg_chop
        +
      • +When calling a function such as <%Package::A::B::function%>, GT::Template will +first attempt to load Package/A/B.pm, then, if it fails, Package/A.pm, and so +on down to Package.pm, looking for Package::A::B::function in each file. This +behaviour is slow and often undesirable - it is recommended to properly split +up packages (that is, putting Package::A::B inside Package/A/B.pm instead of +Package/A.pm or Package.pm). The ``package chopping'' occurs if pkg_chop is set +to 1 (currently the default, but may change), and does not occur if pkg_chop is +set to 0 (recommended, but not the default for historic reasons). +

        +
      • heap
        +
      • +If this is set, it will be added to the end of any other arguments passed to +functions called. +

        +
      • func_code
        +
      • +When calling a function such as <%Package::function%>, you can override the +default behaviour of simply calling the function by providing a code reference +to func_code. Instead of calling Package::function(), your code reference +will be called with the string of the package to call (e.g. +'Package::function') and the arguments that would have been passed to the +function. The return value of your code will be used as if it was the return +value from the real function. +

        +
      • begin
        +
      • +
      • end
        +
      • +begin and end can be used to change the characters that start and end a +template tag. These default to <% for begin, and %> for +end. For example, if you changed begin to [* and end to *], you +would use [*tagname*] for a normal tag, [*-- comment --*] for a comment, +etc. +

      +

      +

      +

      Aliases

      +

      The forth option to parse is an optional hash of aliases to set up for +functions. The key should be the alias name and the value should be the +function to call when the alias is invoked. For example:

      +
      +    print GT::Template->parse(
      +        'file.htm',
      +        { key => 'value' },
      +        { compress => 1 },
      +        { myfunc => 'Long::Package::Name::To::myfunc' }
      +    );
      +

      Now in your template you can do:

      +
      +    <%myfunc('argument')%>
      +

      Which will call Long::Package::Name::To::myfunc.

      +

      +

      +

      vars

      +

      Accessing variables from outside a template can be done by calling the +GT::Template->vars method. For further details, please see +the GT::Template::Vars manpage.

      +

      +

      +
      +

      EXAMPLES

      +

      Parse the string contained in $template, making the 'key' tag available.

      +
      +    my $parsed = GT::Template->parse(undef, { key => 'value' }, { string => $template });
      +

      Parse file.txt, compress the result, and print it. This is equivelant to +print GT::Template->parse(...), but slightly faster.

      +
      +    GT::Template->parse_print('file.txt', { key => 'value' }, { compress => 1 });
      +

      Print the output of the template it as it is parsed, not after entirely parsed. +This will output the same as the above command would without the ``compress'' +option, but is slower (unless, of course, streaming is needed).

      +
      +    GT::Template->parse_stream('file.txt', { key => 'value' });
      +

      Don't display warnings on invalid keys:

      +
      +    GT::Template->parse_print('file.txt', { key => 'value' }, { strict => 0 });
      +

      +

      +
      +

      SEE ALSO

      +

      the GT::Template::Tutorial manpage - Documentation/tutorial for GT::Template template +tags.

      +

      the GT::Template::Vars manpage - Interface for accessing/manipulating template tags from +Perl code.

      +

      the GT::Template::Inheritance manpage - Documentation for GT::Template template +inheritance.

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2005 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Template.pm,v 2.142 2005/07/05 00:39:40 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/Template/Editor.html b/site/glist/templates/help/GT/Template/Editor.html new file mode 100644 index 0000000..5dcaa06 --- /dev/null +++ b/site/glist/templates/help/GT/Template/Editor.html @@ -0,0 +1,311 @@ + + + +GT::Template::Editor - This module provides an easy way to edit templates. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Template::Editor - This module provides an easy way to edit templates.

      +

      +

      +
      +

      SYNOPSIS

      +

      Should be called like:

      +
      +    require GT::Template::Editor;
      +    my $editor = new GT::Template::Editor (
      +                    root        => $CFG->{admin_root_path} . '/templates',
      +                    default_dir => $CFG->{build_default_tpl},
      +                    backup      => 1,
      +                    cgi         => $IN
      +                );
      +    return $editor->process;
      +

      and it returns a hsah ref of variables used for displaying a template editor page.

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Editor.pm,v 2.19 2004/10/19 23:34:44 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/Template/Inheritance.html b/site/glist/templates/help/GT/Template/Inheritance.html new file mode 100644 index 0000000..a242151 --- /dev/null +++ b/site/glist/templates/help/GT/Template/Inheritance.html @@ -0,0 +1,445 @@ + + + +GT::Template::Inheritance - Provides GT::Template inheritance/local file +determination. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Template::Inheritance - Provides GT::Template inheritance/local file +determination.

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::Template::Inheritance;
      +
      +    my $file = GT::Template::Inheritance->get_path(
      +        file => "foo.htm",
      +        path => "/path/to/my/template/set"
      +    );
      +
      +    my @files = GT::Template::Inheritance->get_all_paths(
      +        file => "foo.htm",
      +        path => "/path/to/my/template/set"
      +    );
      +
      +    my @paths = GT::Template::Inheritance->tree(
      +        path => "/path/to/my/template/set"
      +    );
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::Template::Inheritance provides an interface to accessing files for +GT::Template template parsing and include handling. It supports following +inheritance directories and respects ``local'' template directories.

      +

      +

      +

      Inheritance

      +

      GT::Template inheritance works by looking for a .tplinfo file in the template +directory (or local/.tplinfo, if it exists). In order for the template +directory to inherit from another template directory, this file must exist and +must evaluate to a hash reference containing an inheritance key. The +following is a possible .tplinfo file contents:

      +
      +    {
      +        inheritance => '../other'
      +    }
      +

      The above example would indicate that files in this template set can be +inherited from the ../other path, relative to the current template set +directory. The inheritance directory may also contain a full path.

      +

      +

      +

      Inheriting from multiple locations

      +

      You may also inherit from multiple locations by using an array reference for +the inheritance value:

      +
      +    {
      +        inheritance => ['../other', '/full/path/to/a/third']
      +    }
      +

      With the above .tplinfo file, files would be checked for in the current path, +then ../other, then any of ../other's inherited directories, then in +third, then in any of third's inherited directories.

      +

      Also keep in mind that ``local'' directories, if they exist, will be checked for +the file before each of their respective directories.

      +

      Assuming that the initial template path was /full/path/one, and assuming +that ../other inherited from ../other2, the directories checked would be +as follows:

      +
      +    /full/path/one/local
      +    /full/path/one
      +    /full/path/one/../other/local            # i.e. /full/path/other/local
      +    /full/path/one/../other                  # i.e. /full/path/other
      +    /full/path/one/../other/../other2/local  # i.e. /full/path/other2/local
      +    /full/path/one/../other/../other2        # i.e. /full/path/other2
      +    /full/path/to/a/third/local
      +    /full/path/to/a/third
      +

      +

      +
      +

      METHODS

      +

      All methods in GT::Template::Inheritance are class methods. Each method takes +a hash of options as an argument.

      +

      +

      +

      get_path

      +

      +

      +

      get_all_paths

      +

      These methods are used to obtain the location of the file GT::Template will +use, taking into account all inherited and ``local'' template directories. The +get_path option will return the path to the file that will be included, while +the get_all_paths option returns the path to all copies of the file found in +the local/inheritance tree. Both methods take a hash containing the following:

      +
      +
      file
      +
      +
      +The name of the file desired. +
      +

      +
      path
      +
      +
      +The template directory at which to start looking for the above file. Depending +on the existance of ``local'' directories and template inheritance, more than +just this directory will be checked for the file. +
      +

      +
      local
      +
      +
      +Optional. Can be passed with a false value to override the checking of ``local'' +directories for files. +
      +

      +
      inheritance
      +
      +
      +Optional. Can be passed with a false value to override the checking of +inheritance directories for files. +
      +

      +

      +

      +

      tree

      +

      This method returns a list of directories that would be searched for a given +file, in the order they would be searched. It takes the path, local, and +inheritance options above, but not the file option.

      +

      +

      +
      +

      SEE ALSO

      +

      the GT::Template manpage

      +

      +

      +
      +

      MAINTAINER

      +

      Jason Rhinelander

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2005 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Inheritance.pm,v 1.7 2005/02/09 20:51:27 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/Template/Parser.html b/site/glist/templates/help/GT/Template/Parser.html new file mode 100644 index 0000000..c6e295d --- /dev/null +++ b/site/glist/templates/help/GT/Template/Parser.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/Template/Parser.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/Template/Tutorial.html b/site/glist/templates/help/GT/Template/Tutorial.html new file mode 100644 index 0000000..3500989 --- /dev/null +++ b/site/glist/templates/help/GT/Template/Tutorial.html @@ -0,0 +1,1233 @@ + + + +GT::Template::Tutorial - Gossamer Threads template syntax documentation + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Template::Tutorial - Gossamer Threads template syntax documentation

      +

      +

      +
      +

      Template Syntax

      +

      At its most basic level, the template parser replaces tags with content. By +default a tag is anything enclosed between <% and %>. For the +purposes of this document, these two values will be assumed.

      +

      +

      +

      Variable Substitution

      +

      At the simplest level of GT::Template templates are simple variable +replacements. For example, consider the following template code:

      +
      +    You are <%age%> years old.
      +

      This would be displayed with '<%age%>' replaced with a value. For example, if +age was given a value of 35, the above would be displayed as:

      +
      +    You are 35 years old.
      +

      A more complex form of variable access is also available, which is covered +later in the +Advanced variables using references +section of this document.

      +

      +

      +

      Sets

      +

      You can set values from within a template by using:

      +
      +    <%set Title = 'Login'%>
      +

      and now <%Title%> will be equal to Login. This is especially useful for +includes, where you could, for example, set a Title variable to a string +that will be displayed in an included template.

      +

      You can also set one variable to the value of another, such as:

      +
      +    <%set title = $return_title%>
      +

      This will set the variable ``title'' with the value of the variable ``return_title.''

      +

      Note that when using `` '' for a string, you may include variables by using the +$variable or ${variable} syntaxes, such as:

      +
      +    <%set title = "A $main_title page: ${secondary_title}Z"%>
      +

      Additionally, the sequence ``\n'', ``\t'', and ``\r'' produce a linefeed character, +tab, and carriage return, respectively. Additionally, a \ may precede any +non-letter/word character to mean just the character:

      +
      +    <%set price_display = "Price:\n\tTotal: \"\$3.40\""%>
      +

      which would set price_display to:

      +
      +    Price:
      +        Total: "$3.40"
      +

      Note that the interpolation of variables and \ escapes do *not* apply when +using 'single quotes' as the string delimiter. In such cases, only \\ and \' +have a special meaning - they are used for a literal \ and ' character.

      +

      +

      +

      Operators

      +

      GT::Template is capable of performing some basic math calculations and one +string-multiple function in templates displaying the results in the parsed template.

      +

      For example, if the 'age' variable is 15, the following tag:

      +
      +    <%age + 10%>
      +

      will display 25 in the template. Besides addition there are the following +operators, which work as expected: + - + * + / + % (remainder) + ^ (raised to the power of)

      +

      The following operators are also worth explaining: +

      +
      +
      +    i/
      +    /N
      +    ~ (Remainder difference)
      +    x (String multiplier)
      +

      i/ performs integral division between the two numbers. For example, '4' i/ 3 +will result in 1, '100' i/ 3 would result in 33, etc.

      +

      /N does not actually use a literal N, instead N should be replaced by a number. +The result will be formatted (and rounded) to N decimal places. For example, +'4' /3 3 would result in: 1.333, while '5' /3 3 would give you: 1.667. +'3' /3 3 would be 1.000.

      +

      Note that i/ and /0 are not the same, as can be illustrated here: +38 i/ '3.8' => 12 - becomes 38 i/ 3 +38 /0 '3.8' => 10 - 38 / 3.8 is calculated, then rounded with 0 decimal place +precision.

      +

      You should be sure of which one you mean to use, or you may end up with +unexpected results.

      +

      ~ is used to get a remainder difference. Where 8 % 5 would return 3, 8 ~ 5 +will return 2. This is calculated as the divisor (5) minus the remainder (3). +This is useful when generating tables in a loop - when you hit the end of the +loop, you want to be able to put an empty cell with a colspan of however many +rows are left. Something like: <%row_num ~ 5%> will give you the proper value.

      +

      As mentioned, there is also one string operator, 'x'. When you use 'x', the +variable (or value as we'll see in a second) will be displayed ``n'' times, where +``n'' is the integral value of the right hand side.

      +

      Assuming that the 'name' variable is 'Jason', this tag:

      +
      +    <%name x 2%>
      +

      will display JasonJason in the parsed template. Like this, it isn't all that +useful because you could simply put <%name%><%name%> in your +template. However, the right hand side may instead use the value of a +variable, such as in this example:

      +
      +    <%name x $print%>
      +

      Assuming that 'name' is still 'Jason', and that 'print' is 3, this would display:

      +
      +    JasonJasonJason
      +

      Though this is useful as is, this is taken a step furthur: the first does not +always have to be a variable. By using 'single quotation marks' or ``double +quotation marks'' we can display fixed text a variable number of times.

      +

      For example:

      +
      +    <%'My Text' x $print%>
      +

      Again assuming that the variable print is 3, this will print: +

      +
      +
      +    My TextMy TextMy Text
      +

      this comes in handy when doing things like indentation.

      +

      The same string quoting interpolation rules mentioned in Sets apply here - +both `` and ' are accepted as delimiters, with different interpolation rules.

      +

      +

      +

      Set modifiers

      +

      You can add, subtract, etc. to your variables with the following syntax:

      +
      +    <%set variable += 3%>
      +

      += can be changed to the following:

      +
      +    += - Adds to a variable
      +    -= - Subtracts from a variable
      +    *= - Multiplies a variable
      +    /= - Divides a variable
      +    %= - Set a variable to a remainder
      +    ^= - Raise a variable to a power
      +    .= - Appends to a string
      +    x= - "Multiplies" a string - "ab" x 3 is "ababab"
      +    ||= - sets a variable if not already set
      +    &&= - sets a variable if already set
      +

      +

      +

      Set + Operators

      +

      You can combine the above operator functions with sets (including sets with modifiers) by simply adding set foo = to the beginning of the operator tag. For example:

      +
      +    <%set foo += 3 * 3%>     # foo is now set to whatever it was, + 9
      +    <%set foo *= 3 + 3%>     # foo has been multiplied by 9
      +    <%set foo = $bar /0 2%>  # foo is now set to half of $bar, rounded to an integer (see /N above)
      +

      +

      +

      Conditionals

      +

      You can use conditionals if, ifnot (or unless), elseif, and else +as in:

      +
      +    <%if age%>
      +        You are <%age%> years old.
      +    <%elseif sex%>
      +        You are <%sex%>.
      +    <%else%>
      +        I know nothing about you!
      +    <%endif%>
      +
      +    <%ifnot login%>
      +        You are not logged in!
      +    <%endif%>
      +
      +    <%unless age%>
      +        I don't know how old you are!
      +    <%endif%>
      +

      If you like you may use elsif instead of elseif (drop the 'e'). +unless and endunless are aliases for ifnot and endif, respectively, +and may be used interchangeably.

      +

      All conditionals must be ended with an endif tag, although may contain any +number of elseif conditionals and/or a single else conditional between +the if and endif tags.

      +

      Conditionals may be nested within each other, to arbitrary depth:

      +
      +    <%if age%>
      +        You are <%age%> years old
      +        <%if sex%>
      +            and you are <%sex%>
      +        <%endif%>
      +    <%endif%>
      +

      +

      +

      Comparisons

      +

      Inside conditionals you can use <, >, <=, >=, +==, !=, lt, gt, le, ge, eq, ne, contains, starts, +and ends. This allows you to do things like:

      +
      +    <%if age == 15%>
      +        You're 15!
      +    <%endif%>
      +

      where the == can be replaced with any operator listed above. If the right hand +side of the equation starts with a '$', the string will be interpolated as a +variable. If you wish to use a string starting with a literal $, you can avoid +this interpolation by adding quotes around the right hand value. The left hand +side is always a variable.

      +

      lt, gt, le, ge, eq, and ne are the alphabetical equivelants +of <, >, <=, >=, ==, and !=, respectively. +In terms of less-than and greater-than comparisons, the comparison is similar +to a dictionary: aa is less than b, but greater than a; 10 is +greater than 1, but less than 2; Z is less than a, due to +capitalization (unless using ilt, ige, etc.). contains will be true +if the variable contains the right-hand side. starts and ends will be +true if the variable starts with, or ends with, respectively, the right-hand +value.

      +

      There are also case-insensitive versions of the string comparisons - they are: +ilt, igt, ile, ige, ieq, ine, icontains, istarts, and +iends. These comparisons work exactly like the versions _without_ the i +except that the comparison is case-insensitive.

      +

      start, istart, end, and iend are aliases for the comparison with an +added s. like and ilike are deprecated aliases for contains and +icontains and should no longer be used.

      +

      +

      +

      Logical Operators

      +

      If statements (including elseif statements) may contain multiple conditions +using one of the two booleans operators or and and. For example:

      +
      +    <%if age and sex and color%>
      +        I know your age, sex and hair color.
      +    <%else%>
      +        I don't have enough information about you!
      +    <%endif%>
      +
      +    <%if age < 10 or age > 90 or status eq banned%>
      +        You are not permitted to view this page.
      +    <%endif%>
      +

      It should be noted that it is currently not possible to mix both or and +and in a single if statement - you may, however, use the same boolean +multiple times in a single statement. (Brackets) are also not currently +supported.

      +

      Internally, if statements will be short-circuited as soon as possible. That +means that for the following tag: + <%if foo = 1 or foo = 2 or foo = 3%> +the following will occur:

      +

      First, variable ``foo'' will be tested to see if it is numerically equal to 1. +If it is, the rest of the checks are aborted since the if will pass regardless. +If it is not, foo = 2 will be checked, and if true, will abort the next check, +and so on until a condition is true or the end of the list of statements is +encountered.

      +

      Likewise with and, except that with and the parser will stop checking as +soon as the first false value is encountered (since a false value means the +entire condition will be false).

      +

      +

      +

      Loops

      +

      Inside your template you can use loops to loop through an array reference, code +reference, or through a fixed set of numbers. If using an array reference, each +element should be either a hash reference or a scalar value, and when using a +code reference every return should be a hash reference or scalar value, or +undef to end the loop. The variables in the hash reference will then be +available for that iteration of the loop, or, if using scalar values, the value +will be available as the <%loop_value%> variable.

      +

      For example:

      +
      +    <%loop people%>
      +        <%if name eq 'Jason'%>
      +            I have <%color%> hair.
      +        <%else%>
      +            <%name%> has <%color%> hair.
      +        <%endif%>
      +    <%endloop%>
      +

      would loop through all values of pens, and for each one would print the +sentence substituting the color of the pen. Also, inside your loop you can use +the following tags:

      +
      +    <%row_num%> - a counter for what row is being looped, starts at 1.
      +    <%first%>   - boolean value that is true if this is the first row, false otherwise.
      +    <%last%>    - boolean value that is true if this is the last row, false otherwise.
      +    <%inner%>   - boolean value that is true if this is not first and not last.
      +    <%even%>    - boolean value is true if row_num is even.
      +    <%odd%>     - boolean value is true if row_num is odd.
      +

      You could use even and odd tags to produce alternating colors like:

      +
      +    <%loop results%>
      +        <tr><td bgcolor="<%if even%>white<%else%>silver<%endif%>">..</td></tr>
      +    <%endloop%>
      +

      Also, you can use <%lastloop%> to abort the loop and skip straight to the +current loop's <%endloop%> tag, and <%nextloop%> to load the next loop +variables and jump back to the beginning of the loop.

      +

      The 6 built-in variables (row_num, first, last, ...) and any variables set via +the loop variable will only be available for the current loop iteration, after +which the variables of the next loop iteration will be set, or, for variables +that exist in one iteration but not the next, the variables that existed prior +to the loop being called will be restored.

      +

      When using array reference-based loops (which are much more common than and +preferred to the alternative code reference-based loops), you can use the +loopvar.length variable which will contain the number of items contained +within the loop.

      +

      To loop through a particular range of numbers, you can use the following syntax:

      +
      +    <%loop 1 to 5%>
      +        ...
      +    <%endloop%>
      +

      This can alternatively be written as:

      +
      +    <%loop 1 .. 5%>
      +        ...
      +    <%endloop%>
      +

      Additionally, either or both of the two values may be a variable, such as:

      +
      +    <%loop $start to $finish%>
      +        ...
      +    <%endloop%>
      +

      Inside the loop, the current value is accessible via the <%loop_value%> +variable, as if you were simply looping over an array of integers. The looping +always occurs in increments of 1, and the start and end values will have any +fractional value truncated.

      +

      +

      +

      Filters

      +

      Filters can be used to alter the appearance of a tag. The general format of +a filter is:

      +
      +    <%filtername variable%>
      +

      Multiple filternames can be chained together as well, such as:

      +
      +    <%filtername1 filtername2 filtername3 variable%>
      +

      The filter closest to the variable name will be applied first. In the above +example, that means filtername3 will be applied, then +filtername2 applied to the result, then filtername1 applied to that +result.

      +

      The filters available include:

      +
      +
      escape_html
      +
      +
      +This filter will perform HTML-escaping of the variable. Specifically, +<, >, &, and " are converted to their HTML equivelants +(&lt;, &gt;, &amp;, and &quot;, respectively). +
      +
      +

      Assuming that somevar contains the value abc&def"ghi, the following:

      +
      +
      +
      +    <%escape_html somevar%>
      +
      +
      +

      will become:

      +
      +
      +
      +    abc&amp;def&quot;ghi
      +
      +
      +

      If the template parser has escape mode turned on, this would cause variables to +be escaped *twice* -- unless, of course, the variable is passed as a scalar +reference.

      +
      +

      +
      unescape_html
      +
      +
      +This filter will unescape the HTML escapes &amp;, &lt;, &gt;, and +&quot; back to their original forms of <, >, &, and ", +respectively. +
      +

      +
      escape_url
      +
      +
      +When adding a value to a URL is necessary, escape_url can be used to convert +characters other than alphanumeric (a-z, A-Z, 0-9) ., and <-> to the +URL-escaped %XX form (where XX is a two-digit hexadecimal value). +
      +
      +

      For example, if you want to use the tag <%name%> in a URL, it is +recommended to instead use <%escape_url name%>. In particular, this +avoids the possibility of the CGI parameters being passed incorrectly when a +variable contains letters such as ?, ;, =, etc.

      +
      +

      +
      unescape_url
      +
      +
      +This filter performs the opposite of escape_url - that is, any sequence of +%XX, where XX is any two hexadecimal characters, and the + character +will be converted to the appropriate character - a single space in the case of +the +. +
      +

      +
      escape_js
      +
      +
      +This filter will safely escape a javascript variable so that it can be used +inside a javascript string delimited with either ``double quotes'' or 'single +quotes'. Specifically, it puts a \ in front of every \, ' and " +character and converts newlines to \n. +
      +
      +
      +    var javascriptVariable = '<%escape_js somevar%>';
      +
      +

      +
      nbsp
      +
      +
      +This filter will display the variable with all whitespace converted to HTML +non-breaking space escapes (&nbsp;). This is particularly useful when +attempting to display something accurately which may contain spaces, or when +attempting to ensure that a value containing spaces does not wrap over multiple +lines. +
      +

      +
      uc
      +
      +
      lc
      +
      +
      +These filters display the variable with all letters changed to uppercase +(uc) or lowercase (lc). +
      +

      +
      ucfirst
      +
      +
      lcfirst
      +
      +
      +These filters convert the first character of the variable to uppercase +(ucfirst) or lowercase (lcfirst). +
      +

      +

      Some filter examples, assuming the following variables have been set:

      +
      +    var1 => "<html>",
      +    var2 => "test&lt;b&gt;&amp;&lt;/b&gt;two",
      +    var3 => "test't<hr>ee",
      +    fname => "john",
      +    lname => "DOE"
      +

      Examples:

      +
      +    Template code                           --> Becomes
      +    =============                               =======
      +
      +    <tag attr="<%escape_html var1%>">       --> <tag attr="&lt;html&gt;">
      +    <%unescape_html var2%>                  --> test<b>&</b>two
      +    var jsVar = '<%escape_js var3%>';       --> var jsVar = 'test\'three';
      +    <a href="a?f=<%escape_url unescape_html var2">
      +                                            --> <a href="a?f=test%3Cb%3E%26%3C%2Fb%3Etwo">
      +    <a onclick="jsVar = '<%escape_html escape_js var3%>'">
      +                                            --> <a onclick="jsVar = 'test\'t&lt;hr&gt;ee'">
      +

      +

      +

      Includes

      +

      You can include other files. Any tags inside the includes will be evaluated as +if the content of the included file were included in the current file (although +there is one exception: unclosed if tags inside the include will be implicitly +ended). Includes can occur anywhere - inside if statements, for loops, other +includes, etc. The following tag:

      +
      +    <%if info%>
      +        <%include info.txt%>
      +    <%else%>
      +        <%include noinfo.txt%>
      +    <%endif%>
      +

      will include either the file info.txt (if info is true) or noinfo.txt (if info +is false or not set). It must be in the template's root directory which is +defined using $obj->root, or '.' by default.

      +

      A useful application of the include tag is to include files inside a loop, as in:

      +
      +    <%loop people%>
      +        <%include person.txt%>
      +    <%endloop%>
      +

      Another useful example is in including a common header or footer to all pages. +If, for example, you have a header.htm that you wish to be included, but it +needs a variable title, you could combine the include with a set, such +as:

      +
      +    <%set Title = 'Login'%>
      +    <%include header.htm%>
      +

      and then in your header.htm:

      +
      +    <html>
      +        <head>
      +            <title><%Title%></title>
      +        </head>
      +

      This would allow you to have different titles, but still include the same +header template on each page.

      +

      GT::Template also supports including a file based on the value of a variable by +specifying the variable name (starting with a $) for the filename:

      +
      +    <%include $var%>
      +

      This allows for more powerful include abilities. For example, if you wanted +to have a single outer template which includes another, variable template you +could use an outer template (for the purposes of this example, assume this +template is named 'outer.html') something like:

      +
      +    <html>
      +      <head><title><%title%></title></head>
      +      <body>
      +        <div style="border: 5px solid black">
      +          <%include $inner_template%>
      +        </div>
      +      </body>
      +    </html>
      +

      Then, inside other templates, you would do:

      +
      +    abc.html:
      +    =========
      +
      +    <%set inner_template = 'abc_content.html'%>
      +    <%set title = 'ABC'%>
      +    <%include layout.html%>
      +
      +    xyz.html:
      +    =========
      +
      +    <%set inner_template = 'xyz_content.html'%>
      +    <%set title = 'XYZ'%>
      +    <%include layout.html%>
      +

      Now both abc.html and xyz.html would show up with the same page layout defined +in layout.html, with a content section coming from their respective +*_content.html templates.

      +

      If an included template does not exist in the current template set, inherited +template sets will be checked as well. For details on template set +inheritance, see the GT::Template::Inheritance manpage.

      +

      +

      +

      Functions

      +

      You can call functions in either the variable substitution or in the +comparison. The function must reside in a package, and you must do the full +qualification.

      +
      +    A script header normally looks like <%CGI::header%>
      +

      which would call CGI::header(). You can pass arguments to this as in:

      +
      +    A script header normally looks like <%CGI::header('text/html')%>.
      +

      Also, you can pass any currently available template variable to the function +by prefixing it with a $, such as:

      +
      +    <%CGI::header($variable)%>
      +

      Multiple arguments may be passed by comma separating the arguments, as in: + <%Mypackage::mysub($age, 'Title')%>

      +

      If a function returns a hash reference, those values will be added to the +current substitution set. Suppose you have a function:

      +
      +    package Mypackage;
      +    sub load_globals {
      +        ..
      +        return { age => 15, color => red };
      +    }
      +

      You could then do:

      +
      +    <%Mypackage::load_globals%>
      +    You are <%age%> years old, with <%color%> hair!
      +

      Functions are loaded while parsing, so calling the function with different +arguments (to set your variables to different values) is possible.

      +

      Since package names can make functions rather long and ugly, you can call +->parse() with an ``alias'' key in the options hash. This key should contain +shortcut => function pairs. For example, if you want to call Foo::Bar::blah() +in your template, you could pass: asdf => 'Foo::Bar::blah', and when <%asdf%> +or <%asdf(...)%> is encountered, Foo::Bar::blah will be called.

      +

      +

      +

      Comparisons with Functions

      +

      You can combine use a function for an if/elseif statement value, as in:

      +
      +    <%if age == My::years_old%>
      +        You are the same age as me!
      +    <%endif%>
      +

      which would call My::years_old() and compare the return value to the value of +the ``age'' variable.

      +

      +

      +

      Sets with Functions

      +

      You may use a function call as the value of a ``set'' instruction to set a +template variable based on the return value of the function. The following +code will set a variable named ``age'' to the return value of Mypackage::age():

      +
      +    <%set age = Mypackage::age%>
      +

      Arguments passed are the same as the arguments to a regular function.

      +

      +

      +

      Comments

      +

      Comments can be used to add comments about a template, or comment out existing +sections of template code. Comments start with the template opening tag +followed by -- (typically <%--) and end with -- followed by the +template closing tag (typically --%>). Additionally, comments may +contain other template tags, including other comments.

      +

      A simple comment:

      +
      +    <%-- This is a comment, and will not be displayed --%>
      +

      A comment example demonstrating included tags and nested comments:

      +
      +    <%--
      +      <%template_tag%>
      +      <%-- This is a comment, and will not be displayed <%-- another comment --%>--%>
      +      <%if test%>example<%endif%>
      +    --%>
      +

      +

      +

      Advanced variables using references

      +

      A more complex form of variable access is available which allows you to access +values contained within hash and/or array reference variables. These variables +can be used anywhere ordinary variables are permitted - including sets.

      +

      For example, assume a variable named ``person'' has been passed to the template +parser with a value of:

      +
      +    {
      +        name => "John Doe",
      +        age => 35,
      +        hair => "brown"
      +    }
      +

      The following example:

      +
      +    <%person.name%> is <%person.age%> and has <%person.hair%> hair.
      +

      Would display:

      +
      +    John Doe is 35 and has brown hair.
      +

      Arrays are accessed in exactly the same way, using the array index (starting +from 0). Assume for the following example that a variable phone has been +provided, with the following value:

      +
      +    ['(555) 555-5678', '(555) 555-6789', '(555) 555-7890']
      +

      The following example:

      +
      +    Primary phone number: <%phone.0%>
      +    Secondary numbers: <%loop phone%><%unless first%><%loop_value%>  <%endunless%><%endloop%>
      +

      Will display:

      +
      +    Primary phone number: (555) 555-5678
      +    Secondary numbers: (555) 555-6789  (555) 555-7890
      +

      Furthurmore, the size of array reference values (such as array reference-based +loops) may be determined by adding .length to the end of the tag. Using the +same ``phone'' value above, the following:

      +
      +    Primary phone number: <%phone.0%>
      +    <%if phone.length > 1%>Secondary numbers: <%loop phone%><%unless first%><%loop_value%>  <%endunless%><%endloop%><%endif%>
      +

      Would display the same content, however if the ``phone'' value contained only a +single value, the ``Secondary numbers'' line would not be displayed at all:

      +
      +    Primary phone number: (555) 555-5678
      +

      Data structures of abitrary depth are supported, and can consist of any +combination of array and hash references.

      +

      Consider the following more complex example, with a people variable set to +the following loop:

      +
      +    [
      +        {
      +            name => "John Doe",
      +            age => 35,
      +            hair => "no",
      +            phone => {
      +                work => "(555) 555-5678",
      +                home => "(555) 555-6789"
      +            }
      +        },
      +        {
      +            name => "Jane Doe",
      +            age => 25,
      +            hair => "brown",
      +            phone => {
      +                work => "(555) 555-5678",
      +                home => "(555) 555-1234"
      +            }
      +        }
      +    ]
      +

      The following template code:

      +
      +    <%loop person%><%row_num%>. <%name%>, <%age%> years of age, <%hair%> hair.  Phone: work: <%phone.work%>, home: <%phone.home%>.
      +    <%endloop%>
      +    The first person on the list, <%person.0.name%>, can be reached at either <%person.0.phone.work%> or <%person.0.phone.home%>.
      +

      Will display:

      +
      +    1. John Doe, 35 years of age, no hair.  Phone: work: (555) 555-5678, home: (555) 555-6789.
      +    2. Jane Doe, 25 years of age, brown hair.  Phone: work: (555) 555-5678, home: (555) 555-1234.
      +
      +    The first person on the list, John Doe, can be reached at either (555) 555-5678 or (555) 555-6789.
      +

      In addition to hash reference support, GT::Config objects and +GT::CGI objects can be accessed as if they were hashes. So with:

      +
      +    config => $gt_config_object,
      +    in => $cgi_object
      +

      The following:

      +
      +    <%config.variable1%>
      +    <%in.parameter1%>
      +

      would access $gt_config_object->{variable1} and +$cgi_object->param('parameter1'). If used in a <%set ...%> +command, the following:

      +
      +    <%set config.variable1 = 4%>
      +    <%set in.parameter1 = 5%>
      +

      would set $gt_config_object->{variable1} to 4, and call +$cgi_object->param('parameter1', '5'), thereby setting it to 5.

      +

      Furthermore, complex variable expressions may contain other variables, such as:

      +
      +    <%set parameter = 'variable1'%>
      +    <%config.$parameter%>
      +

      which would display the same thing as <%config.variable1%>. Note +that complex variables cannot be used here - that is:

      +
      +    <%set abc.def = 'ghi'%>
      +    <%config.$abc.def%>
      +

      will read the value of abc, then access config.(value).def. It is, however, +possible to put a . inside a value, such as:

      +
      +    <%set abc = 'def.ghi'%>
      +    <%config.$abc%>
      +

      would be equivalent to:

      +
      +    <%config.def.ghi%>
      +

      Complex variables can be used inside double quoted strings (wherever +double-quoted strings are accepted) but only in the form ${a.b}. Specifically, +the following:

      +
      +    <%set var1.key1 = 3%><%set var2 = "$var1.key1"%>
      +

      does not actually set var2 to ``3'' - the following achieves the desired effect:

      +
      +    <%set var1.key1 = 3%><%set var2 = "${var1.key1}"%>
      +

      Complex variables containing other variables are also supported, such as:

      +
      +    <%set var1 = "${var1.$keyvar}"%>
      +

      +

      +

      Whitespace compression

      +

      Normally, template tags do not affect the whitespace around them, however it is +sometimes desirable or necessary to remove the whitespace immediately before or +immediately after the tag. Consider the following example, which is intended +to print the values in 'values' that are odd and less than 10, separated by +-'s:

      +
      +    <%set found_one = 0%>
      +    <%loop values%>
      +        <%set odd = loop_value % 2%>
      +        <%if loop_value < 10 and odd%>
      +            <%if found_one%>
      +                <%-- We've seen one before, so print the '-' --%>
      +                -
      +            <%else%>
      +                <%-- We haven't seen one, so don't print the '-', but print it next time --%>
      +                <%set found_one = 1%>
      +            <%endif%>
      +            <%loop_value%>
      +        <%endif%>
      +    <%endloop%>
      +

      will actually produce, given a loopvar containing the values 1 through 15, +the following output (where _'s are actually spaces):

      +
      +    
      +    
      +    ____
      +    ____
      +    ________
      +    ____________
      +    ____________
      +    ________
      +    ________1
      +    ____
      +    
      +    ____
      +    ____
      +    
      +    ____
      +    ____
      +    ________
      +    ____________
      +    ____________-
      +    ________
      +    ________3
      +    ____
      +    
      +    ____
      +    ____
      +    
      +    ____
      +    ____
      +    ________
      +    ____________
      +    ____________-
      +    ________
      +    ________5
      +    ____
      +    
      +    ____
      +    ____
      +    
      +    ____
      +    ____
      +    ________
      +    ____________
      +    ____________-
      +    ________
      +    ________7
      +    ____
      +    
      +    ____
      +    ____
      +    
      +    ____
      +    ____
      +    ________
      +    ____________
      +    ____________-
      +    ________
      +    ________9
      +    ____
      +    
      +    ____
      +    ____
      +    
      +    ____
      +    ____
      +    
      +    ____
      +    ____
      +    
      +    ____
      +    ____
      +    
      +    ____
      +    ____
      +    
      +    ____
      +    ____
      +

      There are, of course, various ways around this - for instance, you could put +all the tags on one line with no spaces between them. However, such an +approach becomes very difficult to look at and use. The alternative is to use +GT::Templates's whitespace compression tags.

      +

      Any tag (including comments) may start or end (or start and end) with a ~ - +if present, leading (if at the beginning of the tag) and/or trailing (if at the +end of the tag) spaces, tabs, and newline characters will be removed. So, you +could change the above code to the following:

      +
      +    <%set found_one = 0 ~%>
      +    <%loop values ~%>
      +    
      +        <%set odd = loop_value % 2 ~%>
      +        <%if loop_value < 10 and odd ~%>
      +            <%if found_one ~%>
      +                <%-- We've seen one before, so print the '-' --~%>
      +                -
      +            <%~ else ~%>
      +                <%-- We haven't seen one, so don't print the '-', but print it next time --~%>
      +                <%set found_one = 1 ~%>
      +            <%endif ~%>
      +    
      +            <%loop_value ~%>
      +        <%endif ~%>
      +    
      +    <%endloop%>
      +

      Assuming the same data, this would give you a result of:

      +
      +    1-3-5-7-9
      +

      The spaces around the tags are not necessary - that is, you could write +<%~tag_name ~%> <%~ tag_name ~ %> <% ~ tagname ~ %> +or any other variants. The style in the example above is used only for +improved readability.

      +

      Note that this option only affects whitespace in the current template, and does +not affect the value of variables or the content of includes. Example:

      +
      +    abc  <%~ varname ~%>  def
      +

      Assuming 'varname' contains the value ' value ', the above will produce the +output:

      +
      +    abc value def
      +

      but not:

      +
      +    abcvaluedef
      +

      Likewise with includes:

      +
      +    abc <%~ include page.html ~%> def
      +

      Assuming page.html contains the following (with a space before ``a'' and after +``z''):

      +
      +     az
      +

      you will get the following output:

      +
      +    abc az def
      +

      or possibly, if the include contains a newline character at the end:

      +
      +    abc az
      +    def
      +

      but not:

      +
      +    abcazdef
      +

      Additionally, you may use the special tag <%~%> to eliminate both +leading and trailing whitespace without needing a tag. For example:

      +
      +    a
      +    <%~%>
      +    b
      +    <% ~ %>
      +    c
      +

      will output:

      +
      +    abc
      +

      +

      +

      Variable Dumping

      +

      Often it is useful to dump the value of all tags available on the current page, +or the value of a single variable. For this, there is a built-in DUMP function +to dump a variable:

      +
      +    <%DUMP%>
      +
      +    <%DUMP variable%>
      +
      +    <%DUMP variable.key%>
      +

      The first example will display a dump of all the tags available wherever the +<%DUMP%> tag is added, while the second and third examples will +display a dump of the 'variable' and 'variable.key' values, respectively.

      +

      DUMP tags should only be used for debugging purposes; they are not intended +to be used on live, publically accessible templates as they may contain +sensitive data that should not be made publically available.

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2005 Gossamer Threads, Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      SEE ALSO

      +

      the GT::Template manpage - for documentation on invoking the template parser.

      +

      the GT::Template::Inheritance manpage - for documentation on template inheritance.

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Tutorial.pod,v 2.11 2005/04/23 23:11:33 brewt Exp $ + +

      + + + + diff --git a/site/glist/templates/help/GT/Template/Vars.html b/site/glist/templates/help/GT/Template/Vars.html new file mode 100644 index 0000000..fbf4f90 --- /dev/null +++ b/site/glist/templates/help/GT/Template/Vars.html @@ -0,0 +1,364 @@ + + + +GT::Template::Vars - Tied hash for template tags handling + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::Template::Vars - Tied hash for template tags handling

      +

      +

      +
      +

      SYNOPSIS

      +
      +    my $vars = GT::Template->vars;
      +    print $vars->{foo};
      +

      +

      +
      +

      DESCRIPTION

      +

      This module is designed to provide a simple interface to GT::Template tags from +Perl code. Prior to this module, the tags() method of GT::Template returned a +hash reference which could contain all sorts of different values - scalar +references, LVALUE references, GT::Config objects, etc. This new interface +provides a tied hash reference designed to aid in retrieving and setting values +in the same way template variables are retrieved and set from templates.

      +

      +

      +
      +

      INTERFACE

      +

      +

      +

      Accessing values

      +

      Accessing a value is simple - just access $vars->{name}. The regular +rules of escaping apply here: if the value would have been HTML-escaped in the +template, it will be escaped when you get it.

      +

      +

      +

      Setting values

      +

      Setting a value is easy - simply do: $vars->{name} = $value;. ``name'' +can be anything GT::Template recognises as a variable, so +$vars->{'name.key'} would set ->{name}->{key} (see +Advanced variables using references in the GT::Template::Tutorial manpage for more +information on complex variables).

      +

      The regular rules of escaping apply here: if escaping is turned on, a value you +set will be escaped when accessed again via $vars or in a template. If you +want to set a tag containing raw HTML, you should set a scalar reference, such +as: $vars->{name} = \$value;.

      +

      +

      +

      Keys, Exists

      +

      You can use keys %$vars to get a list of keys of the tag object, but you +should note that while $vars->{"a.b"} is valid and +exists $vars->{"a.b"} may return true, it will not be present in the +list of keys returned by keys %$vars.

      +

      +

      +
      +

      SEE ALSO

      +

      the GT::Template manpage

      +

      the GT::Template::Tutorial manpage

      +

      +

      +
      +

      MAINTAINER

      +

      Jason Rhinelander

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2005 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Vars.pm,v 1.3 2005/03/05 01:17:20 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/Text/Tools.html b/site/glist/templates/help/GT/Text/Tools.html new file mode 100644 index 0000000..cfffab9 --- /dev/null +++ b/site/glist/templates/help/GT/Text/Tools.html @@ -0,0 +1,272 @@ + + + +/tmp/glist/private/lib/GT/Text/Tools.pm + + + + + + + + + +

      + + + + + + + diff --git a/site/glist/templates/help/GT/WWW.html b/site/glist/templates/help/GT/WWW.html new file mode 100644 index 0000000..cf0ca86 --- /dev/null +++ b/site/glist/templates/help/GT/WWW.html @@ -0,0 +1,723 @@ + + + +GT::WWW - Multi-protocol retrieving and posting, related in functionality to +LWP. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::WWW - Multi-protocol retrieving and posting, related in functionality to +LWP.

      +

      +

      +
      +

      DESCRIPTION

      +

      GT::WWW is designed to provide a common interface for multiple protocols (as of +this writing, only HTTP and HTTPS, however others are planned) and handles +HEAD, GET, and POST requests. For non-HTTP-based protocols, what, exactly, a +``HEAD'', ``GET'', or ``POST'' request is depends on the module in question. For +example, with FTP ``GET'' might download a file, while ``POST'' might upload one to +the server, and ``HEAD'' might return just the size of the file.

      +

      The modules under GT::WWW should not be used directly; this module should be +used instead. The documentation here describes the options common to all +protocols - however you should check the POD of the protocol subclasses +(GT::WWW::http, GT::WWW::https, etc.) to see any extra options or methods that +those modules provide.

      +

      +

      +
      +

      SYNOPSIS

      +

      Quick way:

      +
      +    use GT::WWW;
      +    my $www = GT::WWW->get("http://server.com/page";);
      +    ...     = GT::WWW->post("http://server.com/page";);
      +    ...     = GT::WWW->head("http://server.com/page";);
      +    ...     = GT::WWW->...("http://user:pass@server.com/page";);
      +    ...     = GT::WWW->...("https://server.com/page");
      +
      +    # This query string will be parsed and passed as POST input:
      +    ...     = GT::WWW->post("http://server.com/page?foo=bar;bar=foo";);
      +

      Longer, but more capable way:

      +
      +    use GT::WWW;
      +    my $request = GT::WWW->new();
      +
      +    $request->protocol("http");
      +    $request->host("server.com");
      +    $request->port(8080);
      +    $request->path("/path/foo.cgi");
      +    $request->username("user");
      +    $request->password("pass");
      +    $request->parameters(foo => "bar", bar => "foo");
      +

      equivelant to the above, using ->url():

      +
      +    $request->url("http://user:pass@server.com:8080/path/foo.cgi?foo=bar;bar=foo";);
      +

      Now call $request->get(), $request->post(), or $request->head().

      +

      Very quick way to print a page:

      +
      +    perl -MGT::WWW=get -e 'print get("http://server.com/page?foo=bar&bar=foo";)'
      +

      +

      +
      +

      METHODS

      +

      Note that all methods that set values (such as host(), port(), etc.) also +return the value when called without any argument.

      +

      +

      +

      new

      +

      Call new() to get a new GT::WWW object. You can call it without arguments to +get a generic GT::WWW object, or use arguments as described below.

      +
      +
      URL
      +
      +
      +You can call new with a single scalar argument - a URL to be parsed. The URL is +of the same format as taken by the url() method. +
      +

      +
      HASH
      +
      +
      +You can alternatively call new() with a hash (or hash reference) of options. +Each of the methods described below can be passed in to new in the form of +key => value pairs - the methods will be called with the values +specified automatically. +
      +

      +

      +

      +

      head

      +

      +

      +

      get

      +

      +

      +

      post

      +

      These are the methods used to tell the module to actually connect to the server +and download the requested page.

      +

      When used as GT::WWW class methods or function calls (but NOT as methods on +GT::WWW objects or sub-objects), they take a single URL as an argument. This +call creates an internal GT::WWW object, turns on +fatal_errors(1), passes the URL to url(), then +calls the appropriate get(), head(), or post() method of the resulting +protocol-specific object.

      +

      Note, however, that once you have specified a protocol (either via +protocol(), or as part of a url passed to url()) +your object ceases to be a GT::WWW object and becomes a protocol-specific +GT::WWW subclass. All subclasses provide their own versions of these methods.

      +

      The subclassed methods are not described here because they may not be supported +for all protocols, and their return value(s) may differ from one protocol to +the next. For more details, see the modules listed in the +SEE ALSO section.

      +

      Generally, get() and post() return an overloaded object that can be used as a +string to get the content (i.e. for printing), but see the notes in the CAVEATS +section of the GT::WWW::http::Response manpage for anything more complicated than +concatenation or printing.

      +

      +

      +

      url

      +

      Takes a URL as argument. The URL is parsed into several fields: protocol, +username, password, host, port, path, and query_string, then +each of those properties are set for the current object. Also note that +calling url() on an existing object resets the host, port, username, password, +and all parameters.

      +

      Interally, this method calls parse_url().

      +

      +

      +

      parse_url

      +

      Takes a URI, and returns the following 7 element list:

      +
      +    #    0          1          2        3      4      5          6
      +    ($protocol, $username, $password, $host, $port, $path, $query_string) =
      +        GT::WWW->parse_url($url);
      +

      URL's require, at a minimum, protocol and host, in URI form:

      +
      +    PROTOCOL://HOST
      +

      The URL can extend up to:

      +
      +    PROTOCOL://USERNAME:PASSWORD@HOST:PORT/PATH?QUERY_STRING
      +

      Only protocols known to GT::WWW are acceptable. To check if a URL is valid, +check $protocol.

      +

      This method can be called as a class or object method, but not as a function. +If called as an object method, the strict option as currently set for the +object will be used; as a class method or function, an optional second +parameter can be passed in - if true, strict query string parsing mode will be +enabled.

      +

      +

      +

      protocol

      +

      Takes a protocol, such as 'http', 'https', 'ftp', etc. Note that when you call +protocol, you object ceases being a GT::WWW object, by becoming a GT::WWW subclass +(such as GT::WWW::http, GT::WWW::https, etc.). Before trying an unknown protocol, +you should generally call the protocol_supported method - calling +protocol(...) with an unsupported protocol will result in a fatal error.

      +

      +

      +

      protocol_supported

      +

      This method takes a protocol, such as 'http', 'https', 'ftp', etc. In order to +make sure the protocol is supported, this checks to see that it is an +internally supported protocol, and also tries to load the module to make sure +that the module can be loaded.

      +

      +

      +

      valid_host

      +

      Returns true in scalar context if the host appears valid, or the host and port +in list context if the host is valid. Note that no check is performed to see +whether or not the host resolves or is reachable - this simply verifies that +the host is at least valid enough to warrant a lookup.

      +

      +

      +

      host

      +

      Sets the host, and optionally the port (assuming the argument is of the form: +'hostname:port'). Returns a fatal error if the host is not valid. Note that +setting the host will reset the port to the protocol's default value, so +this method must be called before port().

      +

      +

      +

      port

      +

      Sets the port for the connection. This can be a name, such as ``smtp'', or a +numeric value. Note that the port value will be reset when the host() +method is called, so setting a port must happen after setting the host.

      +

      +

      +

      reset_port

      +

      Resets the port so that the next request will use the default port.

      +

      +

      +

      username

      +

      Sets or retrieves the login username.

      +

      +

      +

      reset_username

      +

      Removes the login username.

      +

      +

      +

      password

      +

      Sets the login password.

      +

      +

      +

      reset_password

      +

      Removes the login password.

      +

      +

      +

      connection_timeout

      +

      Specifies a timeout for connections, in seconds. By default, a value of 10 is +used. If you specify a false value here, the connection time out will be +system dependent; typically this is from one to several minutes. Note, +however, that the timeout is not supported on Windows systems and so should not +be depended on in code that runs on Windows systems.

      +

      +

      +

      path

      +

      Sets the path for the request. Any HTTP escapes (e.g. %20) are automatically +converted to the actual value (e.g. `` ''). If required, the path will be +automatically re-escaped before being sent to the server.

      +

      +

      +

      parameters

      +

      Takes a list (not a hash, since duplicate keys are permitted) of key => value +pairs. Optionally takes an extra argument - if true, the parameters are added, +not replaced - if omitted (or false), any existing parameters are deleted.

      +

      To specify a valueless parameter without a value, such as b in this example +query string:

      +
      +    a=1&b&c=3
      +

      Pass undef as b's value. Passing ``'' as the value will result in:

      +
      +    a=1&b=&c=3
      +

      For example, to set to two query strings above would require the following two +sets of arguments, respectively:

      +
      +    $www->parameters(a => 1, b => undef, c => 3);
      +
      +    $www->parameters(a => 1, b => "", c => 3);
      +

      To then add a ``d=4'' parameter to either one, you would call:

      +
      +    $www->parameters(d => 4, 1);
      +

      Omitting the extra ``1'' would cause you to erase the previously set parameters.

      +

      Values specified should not be URL encoded.

      +

      If called without arguments, the list of key/value pairs is returned.

      +

      +

      +

      reset_parameters

      +

      Resets the parameters. You want to make sure you do this between each request +on the same object, unless using url(), which calls this for you.

      +

      +

      +

      query_string

      +

      This function serves the same purpose as parameters(), except +that it takes a query string as input instead of a list. Like parameters(), +the default behaviour is to replace any existing parameters unless a second, +true argument is provided.

      +

      Note that if you already have your parameters in some sort of list, it is +preferable to pass them to parameters() than to join them into a query +string and pass them into this function, because this function just splits them +back up into a list again.

      +

      You can also provide a query string (along with a host, path, and possibly +other data) using the url() method.

      +

      If called without arguments, the current parameters will be joined into a valid +query string and returned.

      +

      +

      +

      strict

      +

      This function is used to tell the GT::WWW object to allow/disallow +standard-violating responses. This has a global effect of allowing query +strings to contain _any_ characters except for ``\r'', ``\n'', and ``#'' - normally, +characters such as /, ?, and various extended characters much be escaped into +%XX format. The strict option may have other protocol-specific effects, +which will be indicated in each protocol's documentation.

      +

      The option defaults to non-strict.

      +

      +

      +

      post_data

      +

      This function allows you to pass in raw data to be posted. The data will not be +encoded. If you pass in a code reference, the data will be posted in chunks.

      +

      +

      +

      agent

      +

      Used to set or retrieve the User-Agent string that will be sent to the server. +If the agent string you pass starts or ends with whitespace or a comma, the +default agent will be added at the beginning of end of the User-Agent string, +respectively. This value is only meaningful to protocols supporting something +similar to the HTTP User-Agent.

      +

      +

      +

      default_agent

      +

      Returns the default user agent string. This will be automatically used if no +agent has been set, or if an agent ending with whitespace is specified. This +value is dependent on the protocol being used, but is typically something like +``GT::WWW::http/1.23''. This method is read-only.

      +

      +

      +

      chunk

      +

      +

      +

      chunk_size

      +

      chunk and chunk_size are used to perform a large download in chunks. The +chunk() method takes a code reference that will be called when a chunk of +data has been retrieved from the server, or a value of undef to clear any +currently set chunk code. chunk_size() takes a integer containing the +number bytes that you wish to retrieve at a time from the server; the chunk +code reference will be called with a scalar reference containing up to +chunk_size bytes.

      +

      Note that when using chunked downloading, the data will not be available using +the normal content retrieval interface.

      +

      Also note that, as of 1.024, the chunk code reference only applies to the next +get() or post() request - after each get() or post() request, the chunk_code is +cleared (in order to avoid self-references and possible memory leaks).

      +

      +

      +

      cancel

      +

      +

      +

      cancelled

      +

      The cancel method can be used in conjunction with the chunk +option to abort a download in progress. The chunk code will not be called +again, and the server connection will be closed. This should be used sparingly +and with care. cancelled simply return a true/false value indicating +whether the operation has been cancelled. This value is reset at the beginning +of each operation.

      +

      Note that cancelling an operation is never performed automatically, and only +happens - if ever - in the chunk code reference, so checking the +cancellation status is rarely needed.

      +

      +

      +

      debug_level

      +

      This is used to set or retrieve the debug level. +0 = no debugging +1 = debugging related to current operation +2 = adds operation details to debugging level 1 +3 = adds data debugging (very large!) to debugging level 2

      +

      When passed as part of a hash to new(), the key for this option can be specified +as debug instead of debug_level.

      +

      +

      +

      error

      +

      This method will return a string containing an error that has occured. Note +that an error may be generated even for methods that _seem_ to be correct - for +example, if a server unexpectedly closes the connection before properly +finishing the transfer, a successful return will result since the transfer was +partially successful, but an error message will still be set.

      +

      +

      +

      fatal_errors

      +

      This method will alter the current object's error handling behaviour such that +any errors that occur will be propogated to fatal errors. It is enabled +automatically when using the quick (i.e. objectless) forms of get(), +head(), and post() methods which have no associated object on which +->error can be called.

      +

      +

      +

      file

      +

      This method is used to create a parameter for uploading a file. It takes +either one or two arguments:

      +

      2 argument form: +First argument is a remote filename, second argument is either a local +filename, or a GLOB reference to an open filehandle.

      +

      1 argument form: +Argument is a filename to read.

      +

      Example usage:

      +
      +    my $file = $www->file("foo.txt");
      +    $www->parameters(foobar => $file, 1);
      +    my $response = $www->post();
      +

      This will upload the file from disk named ``foo.txt'', using a form parameter +named ``foobar''. This is similar to uploading a file named ``foo.txt'' via the +following HTML element:

      +
      +    <input type="file" name="foobar">
      +

      The two argument form with two filenames is used to lie to the server about the +actual name of the file. Using a filehandle as the second argument is for use +when a filename is not available - such as an opened socket, or a file that has +been opened elsewhere in the code.

      +

      Examples:

      +
      +    my $file = $www->file("foo.txt", "bar.txt");
      +    my $file2 = $www->file("foo2.txt", \*FH);
      +    $www->parameters(foobar => $file, foobar2 => $file2, 1);
      +    my $response = $www->post();
      +

      This will upload two files - a file named foo.txt (which is actually read +from the bar.txt file) specified as form parameter foobar, and a second +file, specified as parameter foobar2, whose content is read from the +filehandle FH.

      +

      +

      +
      +

      SEE ALSO

      +

      the GT::WWW::http manpage +the GT::WWW::https manpage

      +

      +

      +
      +

      MAINTAINER

      +

      Jason Rhinelander

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: WWW.pm,v 1.25 2005/04/08 19:25:31 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/WWW/http.html b/site/glist/templates/help/GT/WWW/http.html new file mode 100644 index 0000000..feb6325 --- /dev/null +++ b/site/glist/templates/help/GT/WWW/http.html @@ -0,0 +1,493 @@ + + + +GT::WWW::http - HTTP interface for GT::WWW + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::WWW::http - HTTP interface for GT::WWW

      +

      +

      +
      +

      SYNOPSIS

      +
      +    use GT::WWW;
      +    my $www = GT::WWW->new();
      +    $www->protocol('http');
      +    # any valid GT::WWW methods here
      +    # ...
      +    my $header = $www->header;
      +    $header->header("Some-Http-Header" => $value);
      +    $header->delete_header("Some-Other-Http-Header");
      +
      +    my $response = $www->get() or die "Could not connect to server: " . $www->error;
      +
      +    my $status = $response->status;
      +    my $response_code = int $status; # For example, 200, 404, 500, etc.
      +    my $response_str  = "$status"; # For example, 'OK', 'Not Found', 'Internal Server Error', etc.
      +
      +    if ($status) {
      +        # This will be true if the status code is a successful one - in other
      +        # words, true for 2xx responses, false for others
      +        print "Response successful. Content:\n$response\n";
      +    }
      +    else {
      +        die "Request was not successful ($response_code $response_str)\n";
      +    }
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::WWW::http handles HTTP connections for GT::WWW. It uses some overloading +to assist in the ease of use without sacrificing functionality.

      +

      This document does not cover the basics of a GT::WWW object; those are covered +by the GT::WWW manpage.

      +

      +

      +
      +

      METHODS

      +

      +

      +

      header

      +

      This method returns the GT::WWW::http::Header object that will be (or has been) +sent to the HTTP server. See the GT::WWW::http::Header manpage for information on using +and manipulating a header object.

      +

      Note that you can add headers without first getting a header object by simply +specifying the headers as arguments to header(). Normally, you would call:

      +
      +    $www->header->header('X-Foo' => 'bar');
      +

      This shortcut allows for:

      +
      +    $www->header('X-Foo' => 'bar');
      +

      Check the GT::WWW::http::Header manpage for valid arguments to the header() method.

      +

      +

      +

      http_10

      +

      This method can be used before initiating a request on the object to force +HTTP/1.0 communication with the HTTP server. By default HTTP/1.1 connections +are used. Note that HTTP/1.1 is strongly recommended as this module supports +keep-alive connections only when using HTTP/1.1. To force HTTP/1.0 +communication, pass a true value to this method, or a false value to use the +default HTTP/1.1 connections. Returns true if HTTP/1.0 connections will be +used.

      +

      +

      +

      strict

      +

      This works as described in GT::WWW. Specifically, in addition to the loose +query string restrictions, this allows relative URL Location: redirects +(HTTP/1.1 specifically states that Location: redirects MUST be absolute).

      +

      +

      +

      no_redirect

      +

      This method is used before a request to indicate that automatic, seemless +redirection should not take place. By default, when a server responds with +an acceptable, properly-formed 3xx response allowing a redirection, this module +will automatically perform the redirection, unless this option has been +enabled. To enable, call this method with a true value, or to disable, a false +value. Returns true if automatic redirection is enabled.

      +

      Note that redirections will only be performed on GET requests.

      +

      +

      +

      redirects

      +

      If redirections are enabled (i.e. the no_redirect option has not been turned +on), you can call the redirects() method to get a list of response objects +created while performing redirections. Typically this will be just one, but +more are possible.

      +

      +

      +

      response

      +

      Returns the response object for the last request. When automatic redirection +is enabled, this will be the response object for the final request. The +response object can be used is multiple ways, which are described below, in the +RETURN VALUES section, and in the GT::WWW::http::Response manpage.

      +

      +

      +

      cancel

      +

      This works as described in cancel in the GT::WWW manpage, with one exception: if cancelling a +request immediately before a redirect takes place, only the current request is +cancelled - the redirect still occurs. Note that cancelling is likely to be a +resource hit in such a case because the connection cannot be reused and a new +one must be established - typically, to the same server.

      +

      +

      +
      +

      RETURN VALUES

      +

      The return values of the get(), head(), +and post() methods are simply the response object for the +request, which can also be obtained by calling the response() +method after completing the request.

      +

      The full documentation for the response object is covered in +the GT::WWW::http::Response manpage, however the below description is provided for a +brief overview. The following examples assume that ``$response'' is an object +that has been obtained by calling get(), head(), post(), or response().

      +

      +

      +

      Status

      +

      The status of the request is available via the ->status method of the response +object. It is made up of three pieces of data - status code, status string, +and success.

      +

      To get the status code (e.g. 500, 200, etc.), simply use the status as a number:

      +
      +    my $status = int($response->status);
      +

      To get the status string (e.g. ``500 Internal Server Error'', ``200 OK''), use the +status as a string:

      +
      +    my $status = "" . $response->status;
      +

      And finally, to get the success of the request, simply use status in boolean +context:

      +
      +    if ($response->status) {
      +

      Success for HTTP is defined by any 200-level response status code. A request +that returns ``200 OK'' will be pass the above if statement, while a request that +returned ``500 Internal Server Error'' will fail.

      +

      +

      +

      Content

      +

      The content of the last request is available by simply using the response +object as a string:

      +
      +    my $content = "$response";
      +

      You should take note, however, that if you are using the +chunk() method no content will be available in this way.

      +

      Also note that the response object is an object, not a string, so anything +beyond basic string comparison/concatenation should not occur on the response +object itself. See CAVEATS in the GT::WWW::http::Response manpage for more details.

      +

      +

      +

      Headers

      +

      The header() method of the response object returns a GT::WWW::http::Header +object which gives you easy access to the headers returned by the server with +the request.

      +

      As a special shortcut, calling header() with arguments will call the +header() method of the header object with the +arguments provided. This allows you to optionally change this:

      +
      +    my $location = $response->header->header('Location');
      +

      into the shorter and clearer:

      +
      +    my $location = $response->header('Location');
      +

      Calling header() without arguments returns the header object for the response.

      +

      +

      +
      +

      SEE ALSO

      +

      the GT::WWW manpage +the GT::WWW::http::Response manpage +the GT::WWW::http::Header manpage +RFC 2616: http://www.ietf.org/rfc/rfc2616.txt

      +

      +

      +
      +

      MAINTAINER

      +

      Jason Rhinelander

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: http.pm,v 1.31 2005/04/08 19:20:00 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/WWW/http/Header.html b/site/glist/templates/help/GT/WWW/http/Header.html new file mode 100644 index 0000000..8308f62 --- /dev/null +++ b/site/glist/templates/help/GT/WWW/http/Header.html @@ -0,0 +1,584 @@ + + + +GT::WWW::http::Header - Module for GT::WWW::http request/response headers. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::WWW::http::Header - Module for GT::WWW::http request/response headers.

      +

      +

      +
      +

      SYNOPSIS

      +

      Typically:

      +
      +    # Assuming $www is a GT::WWW::http object
      +    my $request_header = $www->header;
      +
      +    # Set a header:
      +    $request_header->header('Some-Http-Header' => 'Header value');
      +
      +    # After making a request:
      +    my $response_header = $www->response->header;
      +    # -- or --
      +    my $response_header = $response->header; # $response is the return of, e.g. $www->get
      +

      Much more advanced headers can be set and determined, using the various methods +available as described below.

      +

      +

      +
      +

      DESCRIPTION

      +

      This module provides an easy to use yet powerful header retrieval/manipulation +object suitable for most HTTP headers.

      +

      +

      +
      +

      METHODS

      +

      First, a note about the methods described which add/change/delete headers: such +methods should only be called on a request header, and only before making a +request. Although nothing prevents you from making changes to the request +header after having made the request, or from changing the headers of a +response header object, such behaviour should be considered very bad practise +and is strongly discouraged.

      +

      +

      +

      header

      +

      This is the most commonly used method as it is used both to add and retrieve +headers, depending on its usage. The examples below assume the following +header:

      +
      +    Date: Sun, 12 Jan 2003 08:21:21 GMT
      +    Server: Apache
      +    Keep-Alive: timeout=15, max=100
      +    Connection: Keep-Alive
      +    Content-Type: text/html
      +    Content-Encoding: gzip
      +    Content-Length: 3215
      +    X-Foo: bar1
      +    X-Foo: bar2, bar3
      +

      With no arguments, a list of all the header names is returned. Given the +example, the following list would be returned:

      +
      +    ('Date', 'Server', 'Keep-Alive', 'Connection', 'Content-Type', 'Content-Encoding', 'Content-Length', 'X-Foo', 'X-Foo')
      +

      With a single argument, a list of value(s) for headers of that name are +returned. In scalar context, only the first value is returned. In list +context, a list of all values is returned. Note that the header named passed +in is case-insensitive.

      +
      +    my $server = $header->header('server'); # returns 'Apache'
      +    my $foo = $header->header('X-Foo'); # returns 'bar1'
      +    my @foo = $header->header('x-Foo'); # returns ('bar1', 'bar2, bar3')
      +

      Finally, when more than one argument is provided, header values are set. At +its simplest level, it takes a list of key => value pairs (NOT a hash, since +duplicate keys are possible) of headers to set. So, to set the headers +'Server' and 'Content-Length' above at once, you could call:

      +
      +    $header->header(Server => 'Apache', 'Content-Length' => 3215);
      +

      Or, if you prefer:

      +
      +    $header->header(Server => 'Apache');
      +    $header->header('Content-Length' => 3215);
      +

      Note that the order in which headers are added is preserved, for times when the +order of headers is important.

      +

      WARNING: Before reading the below information, you should first know that it +describes advanced usage of the header() method and requires have a grasp of +the intricacies of HTTP headers; the following is _not_ required knowledge for +typical GT::WWW use.

      +

      Consider the above Keep-Alive header an example. Instead of specifying:

      +
      +    $header->header('Keep-Alive' => 'timeout=15, max=100');
      +

      you could alternately write it as:

      +
      +    $header->header('Keep-Alive' => [timeout => 15, max => 100]);
      +

      This allows you a more pragmatic approach when you already have some sort of +data structure of the header options. You can go a step further with this, by +specifying undef as the value:

      +
      +    # Set the second X-Foo header in the example:
      +    $header->header('X-Foo' => [bar2 => undef, bar3 => undef]);
      +

      header() also allows you to set values such as:

      +
      +    image/gif;q=0.2
      +

      As can be seen in this example:

      +
      +    Accept: image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
      +

      To do so, specify the suboption value as another array reference. The first +element of the array reference is usually undef, while the remaining are the +k=v pairs in the segment. So, in the above header, the 'image/gif;q=0.2' section +would be specified as:

      +
      +    'image/gif' => [undef, q => 0.2]
      +

      (If a segment such as ``foo=bar;bar=foo'' is ever needed, the undef would be +changed to "bar".)

      +

      So, piecing it all together, the Accept header shown above could be specified +like this:

      +
      +    $header->header(
      +        Accept => [
      +            'image/png'  => undef,
      +            'image/jpeg' => undef,
      +            'image/gif'  => [undef, q => 0.2],
      +            '*/*'        => [undef, q => 0.1]
      +        ]
      +    );
      +

      +

      +

      header_words

      +

      When you need to see it a header value contains a particular ``word'', this +method is the one to use. As an example, consider this header:

      +
      +    X-Foo: bar, bar2, bar3
      +

      In order to determine whether or not ``bar2'' has been specified as an X-Foo +value, you could attempt some sort of regex - or you could just call this +method. The return value splits up the header in such a way as to be useful to +determine the exact information contained within the header.

      +

      The method takes a case-insensitive header name, just like the single-argument +form of header().

      +

      A even-numbered hash-like list is always returned - though each element of +that list depends on the content of the header. First of all, if the header +specified does not exist, you'll get an empty list back.

      +

      Assuming that the header does exist, it will first be broken up by ,.

      +

      The even-indexed (0, 2, 4, ...) elements of the list are the keys, while the +odd numbered elements are the values associated with those keys - or undef if +there is no value (as above; an example with values is shown below).

      +

      So, using the above X-Foo header example, calling this method with 'X-Foo' +as an argument would give you back the list:

      +
      +    (bar => undef, bar2 => undef, bar3 => undef)
      +

      Getting a little more complicated, consider the following header:

      +
      +    X-Foo: bar, bar2=foo, bar3
      +

      Because of the ``=foo'' part, the list returned would now be:

      +
      +    (bar => undef, bar2 => "foo", bar3 => undef)
      +

      Quoting of values is also permitted, so the following would be parsed correctly +with '1;2,3=4"5\6' being the value of bar2:

      +
      +    X-Foo: bar, bar2="1;2,3=4\"5\\6", bar3
      +

      Getting more complicated, this method also handles complex values containing +more than one piece of information. A good example of this is in content type +weighting used by most browsers. As a real life example (generated by +the Phoenix web browser):

      +
      +    Accept: video/x-mng,image/png,image/jpeg,image/gif;q=0.2,*/*;q=0.1
      +

      Working that into the X-Foo example, consider this header:

      +
      +    X-Foo: bar, bar2=foo, bar3;foo1=24;foo2=10
      +

      In this case, the value for bar3 will become an array reference to handle the +multiple pieces of information in the third part:

      +
      +    (bar => undef, bar2 => "foo", bar3 => [undef, foo1 => 24, foo2 => 10])
      +

      (If you've read the advanced section of the header() +documentation, and this looks familiar, you're right - the return value of this +function, if put in an array reference, is completely compatible with a +header() value.)

      +

      The undef value at the beginning of the array reference is rarely anything other +than undef, but it could be, if a header such as this were encountered:

      +
      +    X-Foo: bar=foo,foo1=10
      +

      That would return:

      +
      +    (bar => ["foo", foo1 => 10])
      +

      One additional thing to note is that header_words() returns the header words +for all matching headers. Thus if the following two headers were set:

      +
      +    X-Foo: bar, bar2=foo
      +    X-Foo: bar3
      +

      You would get the same return as if this header was set (shown above):

      +
      +    X-Foo: bar, bar2=foo, bar3
      +

      A good example usage of this is for a file download. To get the filename, you +would do something like:

      +
      +    my %cd = $header->header_words('Content-Disposition');
      +    my $filename;
      +    if ($cd{filename}) { $filename = $cd{filename} }
      +    else               { $filename = "unknown" }
      +

      +

      +

      split_words

      +

      This can be called as object method, class method, or function - it takes a +single argument, a string, which it proceeds to split up as described for the +above header_words() method. Note that this knows nothing about header names - +it simply knows how to break a header value into the above format.

      +

      This method is used internally by header_words(), but can be used separately if +desired.

      +

      +

      +

      contains

      +

      This method takes two arguments: a header, and a header word. It returns true +if the header word passed is found in the header specified. For example, the +following would return true:

      +
      +    $header->contains('X-Foo' => 'bar2')
      +

      for any of these headers:

      +
      +    X-Foo: bar2
      +    X-Foo: bar, bar2, bar3
      +    X-Foo: bar, bar2=10, bar3
      +    X-Foo: bar, bar2=10;q=0.3, bar3
      +

      but not for either of these:

      +
      +    X-Foo: bar, bar3=bar2
      +    X-Foo: bar, bar3;bar2=10
      +

      +

      +

      join_words

      +

      join_words() does the opposite of split_words(). That is, it takes a value such +as might be returned by split_words(), and joins it up properly, quoting if +necessary. This is called internally when creating the actual header, and can +be called separately at a method or function if desired.

      +

      +

      +

      delete_header_word

      +

      This takes a header and header word, and proceeds to remove any occurances of +the header word from the header specified.

      +

      After calling:

      +
      +    $header->delete_header_word('X-Foo', 'bar2');
      +

      this header:

      +
      +    X-Foo: bar, bar2;foo=bar, bar3
      +

      would become:

      +
      +    X-Foo: bar, bar3
      +

      +

      +

      delete_header

      +

      This takes a list of header names. The headers specified are completely +removed. +

      +
      +
      +=head2 replace_header
      +

      This 2 or more arguments in exactly the same way as header(), however all the +specified headers are deleted (assuming they exist) before being readded.

      +

      +

      +

      format_headers

      +

      This returns a properly formatted (lines are CRLF delimited) header. If you +use the header as a string (i.e. "$header"), this method will be internally +called, and so generally does not need to be called directly.

      +

      The returned string has the final blank line that identifies the end of the +header.

      +

      +

      +

      clear_headers

      +

      This deletes all headers.

      +

      +

      +
      +

      SEE ALSO

      +

      the GT::WWW::http manpage +the GT::WWW manpage +RFC 2616: http://www.ietf.org/rfc/rfc2616.txt

      +

      +

      +
      +

      MAINTAINER

      +

      Jason Rhinelander

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Header.pm,v 1.8 2004/02/17 01:33:08 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/WWW/http/Response.html b/site/glist/templates/help/GT/WWW/http/Response.html new file mode 100644 index 0000000..54e8245 --- /dev/null +++ b/site/glist/templates/help/GT/WWW/http/Response.html @@ -0,0 +1,478 @@ + + + +GT::WWW::http::Response::Status - Overloaded +response objects for HTTP request data. + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::WWW::http::Response and GT::WWW::http::Response::Status - Overloaded +response objects for HTTP request data.

      +

      +

      +
      +

      SYNOPSIS

      +
      +    # ($www is continued from GT::WWW::http SYNOPSIS)
      +
      +    my $response = $www->get(); # or post(), or head()
      +    # -- or, after having called get(), post() or head(): --
      +    my $response = $www->response();
      +
      +    my $status   = $response->status();
      +
      +    my $content = "$response";
      +    my $response_code = int($status); # i.e. 200, 404, 500
      +    my $response_str = "$status"; # i.e. 'OK', 'Not Found', 'Internal Server Error'
      +    if ($status) { # True for 2xx requests, false otherwise (e.g. 404, 500, etc.)
      +        ...
      +    }
      +

      +

      +
      +

      DESCRIPTION

      +

      GT::WWW::http::Response objects are returned by the get(), +post(), and head() methods of GT::WWW +HTTP requests (and derivatives - i.e. HTTPS), or by calling +response() after having made such a request. The +objects are overloaded in order to provide a simple interface to the response, +while still having all the information available.

      +

      A response object always returns true in boolean context, allowing you to do +things like $www->get($url) or die; - even when a page is empty, or +contains just '0'.

      +

      +

      +
      +

      CONTENT

      +

      In addition to the methods described below, the way to simply access the data +returned by the server is to simply use it like a string - for example, +printing it, concatenating it with another string, or quoting it.

      +

      You should, however, take note that when using the chunk() +option for an HTTP request, the content will not be available.

      +

      +

      +
      +

      METHODS

      +

      For simple requests, often the content alone is enough. The following methods +are used to determine any other information available about the response.

      +

      +

      +

      content

      +

      Returns the content of the HTTP response. Note that this returns the exact +same value as using the object in double quotes.

      +

      +

      +

      status

      +

      Returns the response status object for the request. This object provides three +pieces of information, and has no public methods. Instead, the data is +retrieved based on the context of the object.

      +
      +    my $status = $response->status;
      +

      (N.B. Though the examples below use a $status variable, there is no reason +they couldn't be written to use $response->status instead.)

      +
      +
      numeric status
      +
      +
      +The numeric status of an HTTP request (e.g. 200, 404, 500) is available simply +by using the status object as a number. +
      +
      +
      +    my $numeric_status = int $status;
      +
      +

      +
      string status
      +
      +
      +The string status of an HTTP request (e.g. ``OK'', ``Not Found'', ``Internal Server +Error'') is available by using the status object as a string (e.g. printing it, +or concatenating it with another string). +
      +
      +
      +    # Assign the status string to a variable:
      +    my $status_string = "$status";
      +
      +
      +
      +    # Print out the status string:
      +    print $status;
      +
      +
      +
      +    # To get a string such as "500 Internal Server Error":
      +    my $string = int($status) . " " . $status;
      +
      +

      +
      boolean status
      +
      +
      +In order to quickly determine whether or not a request was successful, you can +use the status object in a boolean context. +
      +
      +

      Success is determined by the numeric status of the response. Any 2xx status +(usually 200 OK, but there are others) counts as a successful response, while +any other status counts as a failure.

      +
      +
      +
      +    if ($status) { print "Request successful!" }
      +    else         { print "Request failed!"     }
      +
      +

      +

      +

      +

      header

      +

      This method, called without arguments, returns the +header object for the response.

      +
      +    my $header = $response->header;
      +

      If this method is called with arguments, those arguments are passed to the +header() method of the header object. This +allows this useful shortcut:

      +
      +    my $some_header_value = $response->header("Some-Header");
      +

      instead of the alternative (which also works):

      +
      +    my $some_header_value = $response->header->header("Some-Header");
      +

      Information on header object usage is contained in the GT::WWW::http::Header manpage.

      +

      Note that although a header object allows for header manipulation, changing the +headers of a response object should be considered bad practise, and is strongly +discouraged.

      +

      +

      +
      +

      CAVEATS

      +

      Although the response object _works_ like a string, keep in mind that it is +still an object, and thus a reference. If you intend to pass the data to +another subroutine expecting a string, it is recommended that you force the +content into string form, either by quoting the variable ("$var") or by +calling the content() method ($var->content). Not doing so can lead to +unexpected results, particularly in cases where another subroutine may +differentiate between a string and a reference, and not just use the value as a +string.

      +

      Also, in terms of speed, obtaining the content (not the object) into another +variable (either via "$var" or $var->content) can make quite a +substantial difference when several string comparison operations are performed. +The reason is simply that every time the object is used is a string, the +content method is called, which can amount to a significant slowdown.

      +

      Although string operations that change the string (i.e. s///) appear to work, +they in fact clobber the reference and turn your variable into an ordinary +string. This should not be done - if the string needs to be modified, take a +copy of it first, and modify the copy.

      +

      +

      +
      +

      SEE ALSO

      +

      the GT::WWW manpage +the GT::WWW::http manpage +the GT::WWW::http::Header manpage +RFC 2616: http://www.ietf.org/rfc/rfc2616.txt

      +

      +

      +
      +

      MAINTAINER

      +

      Jason Rhinelander

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: Response.pm,v 1.8 2004/08/04 19:23:07 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/GT/WWW/https.html b/site/glist/templates/help/GT/WWW/https.html new file mode 100644 index 0000000..cdc158a --- /dev/null +++ b/site/glist/templates/help/GT/WWW/https.html @@ -0,0 +1,322 @@ + + + +GT::WWW::https - HTTPS handling for GT::WWW + + + + + + + + + +

      + + + + + +
      +

      +

      +

      NAME

      +

      GT::WWW::https - HTTPS handling for GT::WWW

      +

      +

      +
      +

      DESCRIPTION

      +

      This module is a simple subclass of GT::WWW::http used by GT::WWW to enable +HTTPS access as opposed to HTTP access. Thus GT::WWW::http should be consulted +instead of this documentation.

      +

      +

      +
      +

      REQUIREMENTS

      +

      GT::WWW HTTPS support requires GT::Socket::Client::SSLHandle, which in turn +requires the Net::SSLeay library.

      +

      +

      +
      +

      SEE ALSO

      +

      the GT::WWW::http manpage

      +

      +

      +
      +

      MAINTAINER

      +

      Jason Rhinelander

      +

      +

      +
      +

      COPYRIGHT

      +

      Copyright (c) 2004 Gossamer Threads Inc. All Rights Reserved. +http://www.gossamer-threads.com/

      +

      +

      +
      +

      VERSION

      +

      Revision: $Id: https.pm,v 1.3 2004/01/13 01:35:20 jagerman Exp $

      + + + + diff --git a/site/glist/templates/help/account.html b/site/glist/templates/help/account.html new file mode 100644 index 0000000..b15518f --- /dev/null +++ b/site/glist/templates/help/account.html @@ -0,0 +1,139 @@ + + + +Gossamer List - Help - Accounts + + + + + + + + + + + + +
      Close +

      + + + + +
      Help - Account
      + + + + +
      + + + + +

      + + + + +
      +
      +

      The "Account" menu allows you to modify your own Gossamer List account and (if you are an administrator) the accounts of your users. The email templates used to build emails sent to your subscribers can also be managed from here. The menu allowing you to modify your own profile is displayed + by default.

      +

      - The values entered in the "First Name", "Last Name", + "Email address", "Company Name" and "URL" fields can be + automatically added to messages sent from Gossamer List + using the following tags:

      +
      +
      +
      +

      <%pro_first_name%>
      + <%pro_last_name%>
      + <%usr_email%>
      + <%pro_company%>
      + <%pro_url%>

      +
      +
      +
      +

      - The "Reply-to-Email" and "Bounce Email" fields contain + the addresses that will be set as the default reply and + bounce email addresses in the "Create Message" menu.

      +

      - The "Date format" drop-down menu controls the date format + that will be used for your messages. To create a custom date format, select one from the drop-down menu and modify the format in the field to the right.

      +

      - The "Editor Advanced" drop-down menu controls the default message format that will appear when creating new messages.

      +

      - Click the "Update Profile" button to save your changes, + or click the "Reset" button to restore the original values. +

      +

      Change Password
      + Clicking the "Change Password" link displays a menu allowing + you to change your Gossamer List password.

      +

      - Enter your current password in the "Old Password" field, + enter your new password in the "New Password" and "Re-type + new" fields and click the "Update Password" button.

      +

      Email Templates
      + Clicking the "Email Templates" link brings up a menu allowing you to edit the templates that will build the subscription, unsubscription andvalidation emails that are sent to your subscribers. Administrators can modify the default content of these templates; read the help section for"Default Email Templates" in the Templates menu for more information. +

      +

      - Select the specific template you wish to modify from + the drop-down menu and click "Load". The email template + will be displayed in the "To", "Subject", "From", "Extra + Body" and "E-mail body" fields.

      +

      - Make changes to the text and tags included in the desired + fields and click the "Save" button. The following tags can + be included in any of the fields:

      +
      +
      +
      +

      <%pro_first_name%> will automatically insert the + value entered in the "First Name" field in the Account + menu.
      + <%pro_last_name%> will automatically insert the + value entered in the "Last Name" field in the Account + menu.
      + <%usr_email%> will automatically insert the value + entered in the "Email" field in the Account menu.
      + <%pro_company%> will automatically insert the value + entered in the "Company Name" field in the Account menu. +
      + <%pro_url%> will automatically insert the value + entered in the "URL" field in the Account menu.
      + <%sub_email%> will automatically insert the subscriber's + email address.
      + <%sub_name%> will automatically insert the subscriber's + name.

      +
      +
      +
      +

      - You can save the template under a different name by entering + one in the "Save template as:" field and clicking the "Save" + button.

      +

      Header & Footer
      +The Header & Footer menu allows you to create headers and footers that will be attached to the tops and bottoms of all messages sent using your Gossamer List account. Whether the HTML or text headers and footers are attached is dependant on whether you are sending a message in HTML or text format. The following tags can be inserted into the HTML headers and footers: +

      +
      +

      User info:
      +- <%%usr_username%>
      +- <%%usr_email%>
      +- <%%pro_first_name%>
      +- <%%pro_last_name%>
      +- <%%pro_url%>

      + +Subscriber info:
      +- <%%sub_name%>
      +- <%%sub_email%>
      + +Unsubcribe URL allowing subscribers to remove themselves from the list:
      +- <%%unsubscribe_url%>

      + +Global tags:
      +- <%%site_title%>
      +- <%%site_url%> + +

      +
      +
      + +
      +
      +
      +
      + + diff --git a/site/glist/templates/help/include_style.css b/site/glist/templates/help/include_style.css new file mode 100644 index 0000000..126ed30 --- /dev/null +++ b/site/glist/templates/help/include_style.css @@ -0,0 +1,15 @@ +.body { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 9pt} +.small { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 7pt} +.object { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 9pt; color: #000000; background-color: #c5e1a3 ; border:1px solid #A1A576;} +.object_white { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 9pt; color: #000000; background-color: #FFFFFF; border: 1px solid #A1A576;} +.button { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 8pt; color:#FFFFFF; background-color:#7a9f54; border:1px solid #008000;} +.highlight { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 9pt; background-color: #CBD5DE } +a:link { color: #003CB7; text-decoration: none} +a:visited { color: #003CB7; text-decoration: none} +a:hover { text-decoration: underline} +a:active { color: #CC0000; text-decoration: none} +.body_bold { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 9pt; font-weight: bold} +.body_white { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 9pt; color: #FFFFFF} +.bold_white { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 9pt; font-weight: bold; color: #FFFFFF} +.help_header { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 12pt; font-weight: bold; color: #006600} +.helptitle { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 10pt; font-weight: bold; color: #336600} diff --git a/site/glist/templates/help/index.html b/site/glist/templates/help/index.html new file mode 100644 index 0000000..67b5382 --- /dev/null +++ b/site/glist/templates/help/index.html @@ -0,0 +1,15 @@ + + + Gossamer Threads Module Library Reference + + + + + <body bgcolor="#FFFFFF"> + + <p>Non frame capabale browser please view the <a href="glist.cgi?do=admin_gtdoc&topic=/index_nav.html">index</a>.</p> + + </body> + + + diff --git a/site/glist/templates/help/index_nav.html b/site/glist/templates/help/index_nav.html new file mode 100644 index 0000000..791563e --- /dev/null +++ b/site/glist/templates/help/index_nav.html @@ -0,0 +1,264 @@ + + + Gossamer Threads Module Library Reference + + + + + + + + +

      Module Index

      +

      GT

        AutoLoader

        Base

        CGI

          Action

            Common

            Plugin

          Cookie

          EventLoop

          Fh

          MultiPart

        Cache

        Config

        Date

        Delay

        Dumper

        File

          Diff

          Tools

        FileMan

          Commands

          Diff

          lang_fr.Commands

        Installer

        MD5

          Crypt

        MIMETypes

        Mail

          BulkMail

          Editor

            HTML

            Text

          Encoder

          Message

          POP3

          Parse

          Parts

          Send

        Plugins

          Author

          Installer

          Manager

          Wizard

        RDF

        SQL

          Admin

          Base

          Condition

          Creator

          Display

            HTML

              Relation

              Table

          Driver

            MSSQL

            MYSQL

            ORACLE

            PG

            Types

            debug

            sth

          Editor

          File

          Monitor

          Relation

          Search

            Base

              Common

              Indexer

              STH

              Search

            INTERNAL

              Indexer

              Search

            MSSQL

              Indexer

              Search

            MYSQL

              Indexer

              Search

              VER3

              VER4

            NONINDEXED

              Indexer

              Search

          Table

          Tree

            Rebuild

          Types

          Upgrade

        Session

          File

          SQL

          TempTable

        Socket

          Client

            SSLHandle

        Tar

        TempFile

        Template

          Editor

          Inheritance

          Parser

          Tutorial

          Vars

        Text

          Tools

        WWW

          http

            Header

            Response

          https

      \ No newline at end of file diff --git a/site/glist/templates/help/lists.html b/site/glist/templates/help/lists.html new file mode 100644 index 0000000..6015f20 --- /dev/null +++ b/site/glist/templates/help/lists.html @@ -0,0 +1,302 @@ + + + +Gossamer List - Help - Lists + + + + + + + + + + + + +
      Close +

      + + + + +
      Help - Lists
      + + + + +
      + + + + +

      + + + + +
      +
      +

      The Lists menu allows you to create and manage your lists + of email addresses, as well as lists created by your users. + HTML allowing people to submit their email addresses to + be added to specific lists can also be generated from the + Lists menu. There are four options within the Lists menu: + Lists (displayed by default when the Lists menu is opened), + Create List, Find List and Subscribers.

      +

      Lists
      + The total number of subscribers in a particular list is + listed beside the number of validated subscribers. Clicking on any of the menu headings ("List Name", "Created", "Subscribers", etc.) allows you to sort the lists by those headings. Clicking + on either of the subscriber numbers will display a record + of those subscribers on the list and allow you to search for other subscribers (see the "Search Subscribers" section below for details on searching). Clicking on a subscriber's name allows you to modify that subscriber (see the "Subscribers" section below for details).

      +

      You can validate or delete subscribers on the list by checking + the boxes beside the desired subscribers (check the box + at the top of the form to select all subscribers) and clicking + either the "Validate Subscribers" or "Delete Subscribers" + button.

      +

      You also have the option of searching for subscribers (see + the "Search Subscribers" section below for details) or importing + subscribers to the list (see the "Import Subscribers" section + below for details).

      +
      +
      + +
      +
      +

      Editing Lists
      +Clicking on a list's name brings up a form allowing you to modify all aspects of it.

      +

      - The list name and templates must have values.

      +

      -The value + in the "Description" field will not be shown to subscribers + subscribing to or unsubscribing from the list. The Description + field is only visible to administrators or users while editing + their lists.

      +

      - The "Require Double Opt In" option toggles whether or + not subscribers will automatically be validated when subscribing + to the list. If it is set to "No", subscribers will be automatically + validated. If it is set to "Yes", subscribers will be sent + a confirmation email allowing them to subscribe.

      +

      - The template options allow you to select which templates + will be used to create the emails that are automatically + sent to subscribers during the subscription process. The + default templates can be modified or new ones can be created + from your Account menu.

      +
      +
      +
      +

      + The "Opt In Template" builds the email that will be + sent to subscribers allowing them to confirm their subscription + if the "Require Double Opt In" option is set to "Yes". + The "validation" template contains this message by default.
      +
      + + The "Subscribe Template" builds the email that will + be sent to subscribers informing them that they have successfully + subscribed to the list. The "subscribe" template contains + this message by default.
      +
      + + The "Unsubscribe Template" builds the email that will + be sent to subscribers after they have unsubscribed from + a list, informing them that they were successfully unsubscribed. + The "unsubscribe" template contains this message by default. +

      +
      +
      +
      +

      - The URL options control where subscribers will be directed + after they have performed certain actions. Clicking the + "Use Sample" button will insert sample locations of + the pages, although you can specify any location you like. +

      +
      +
      +
      +

      + The Success-Subscribe field contains the URL to which subscribers will besent when they have successfully subscribed to the list.

      +

      + The Success-Unsubscribe field contains the URL to which subscribers willbe sent when they have successfully unsubscribed from the list.

      +

      + The Success-Validate field contains the URL to which subscribers will besent after their subscription to the list has been validated.

      +

      + The Failure-Subscribe field contains the URL to which subscribers will besent if they were not able to successfully subscribe to the list.

      +

      + The Failure-Unsubscribe field contains the URL to which subscribers willbe sent if they were not able to successfully unsubscribe from the list.

      +
      +
      +
      +

      Click the "Update a List" button to save your modifications, + or click the "Reset" button to restore the options to their + original settings.

      +

      Generating List HTML
      + While modifying a list or looking at its subscribers you can click the "HTML" link to display + a window containing HTML that can be added to a webpage + to create a form allowing subscribers to subscribe to the + list.
      +

      Import Subscribers
      + While modifying a list or looking at its subscribers you can click the "Import" link to display a form allowing you to import subscribers to a list from a + text file or from a list.

      +

      - By default the list you were viewing/modifying will be selected, but you can choose to import subscribers to any list(s). Select the list(s) you wish to add subscribers to by clicking on the desired lists in the top menu (hold "Ctrl" while clicking to select multiple lists).

      +

      - If you are importing subscribers from a text file, enter + the path to it in the "From file" field, or use the "Browse" + button to select it.

      +

      - If you are importing users from a list that you will + type in or copy and paste from somewhere else, enter the + email addresses (and subscriber names if desired) in the + "Subscribers" field.

      +

      -If you are importing subscriber names as well as email + addresses, check the "Subscriber Name" box in the "Fields + To Import" section (the "Subscriber Email" box cannot be + unchecked).

      +

      - If the subscriber data you are importing uses a delimiter + (a character or a set of characters inserted between fields + to keep them separate) to separate the subscriber names + from the subscriber emails, enter the delimiter in the "Fields: + Delimiter" box.

      +

      - If you have used a delimiter which appears within any + of the subscriber data you are importing as a regular character + (if, for example, you use the dash character as a delimiter + and some of your subscribers' email addresses contain dashes), + you will have to provide an escape character in the field + provided. The escape character will have to be entered before + every delimiter character in the import data that you do + not want to be recognized as a delimiter (the escape character + would have to be entered before every dash used in subscribers' + email addresses in the above example). This will prevent + the import from using the dashes in the email addresses + as delimiters and the escape character will be removed, + leaving the email addresses and their dashes intact.

      +

      - If you have selected an escape character which appears + within any of the subscriber data as a regular character, + you will have to add another escape character before it + (two consecutive underscore characters would have to be + entered in order for one underscore to appear in the import + data if the underscore character was set as the escape character). +

      +

      - If the escape character appears in front of the delimiter + character as regular characters in any of the import data + (a colon followed by an underscore within a subscriber's + email address), two additional escape characters will have + to be entered in front of the escape character and delimiter + in order to keep the data intact. For the above examples, + three underscore characters followed by a dash would be + imported as one underscore and one dash.

      +

      - If the subscriber data you are importing uses a delimiter + to separate records from one another, enter the delimiter + in the "Records: Delimiter" box.

      +

      - If the data you are importing contains a description + or some other information that you do not wish to import, + you can enter an "Ignore first lines" value in the field + provided, specifying how many of the first lines of the + data you do not wish to import.

      +

      Click the "Import" button to perform the import, or click + the "Reset" button to clear the import menu.

      +

      Create List
      + Clicking the "Create List" link brings up a menu allowing + you to define all aspects of a new list of subscribers. +

      +

      - Provide the list with a name in the field provided. This + field must be completed.

      +

      - Enter a description which will be displayed when you + are reviewing and editing your lists.

      +

      - The "Require Double Opt In" option toggles whether or + not subscribers will automatically be validated when subscribing + to the list. If it is set to "No", subscribers will be automatically + validated. If it is set to "Yes", subscribers will be sent + a confirmation email allowing them to subscribe.

      +

      - The template options allow you to select which templates + will be used to create the emails that are automatically + sent to subscribers during the subscription process. The + default templates can be modified or new ones can be created + from the Templates menu.

      +
      +
      +
      +

      + The "Opt In Template" builds the email that will be + sent to subscribers allowing them to confirm their subscription + if the "Require Double Opt In" option is set to "Yes". + The "validation" template contains this message by default. +
      +
      + + The "Subscribe Template" builds the email that will + be sent to subscribers informing them that they have successfully + subscribed to the list. The "subscribe" template contains + this message by default.

      +

      + The "Unsubscribe Template" builds the email that will + be sent to subscribers after they have unsubscribed from + a list, informing them that they were successfully unsubscribed. + The "unsubscribe" template contains this message by default. +

      +
      +
      +
      +

      - The URL options control where subscribers will be directed + after they have performed certain actions. Clicking the + "Use Sample" button will insert sample locations of + the pages, although you can specify any location you like. +

      +
      +
      +
      +

      + The "URL Subscribe Success" field contains the URL + to which subscribers will be sent when they have successfully + subscribed to the list.

      +

      + The "URL Unsubscribe Success" field contains the URL + to which subscribers will be sent when they have successfully + unsubscribed from the list.

      +

      + The "URL Failure" field contains the URL to which subscribers + will be sent if they were not able to successfully subscribe + to or unsubscribe from the list.

      +
      +
      +
      +

      Click the "Create a List" button to create the list, or + click the "Reset" button to blank all fields.

      + + + +

      +

      Find Lists
      + Clicking the "Find Lists" link will display a detailed + form allowing you to search for lists you've created.

      +

      - Enter values for the lists you are searching for (List + ID, date created, etc.), and define your search parameters + in the bottom section of the form.

      +

      - You can define the maximum number of lists you want the + search to display in the "Maximum Hits" field.

      +

      - If the "Match Any" box is checked the search will return + all lists containing any of the search criteria entered + in the search form. For example, a "Match Any" search for + lists with "2" in the List ID field and "admin" in the Users + Email field would return all lists which contain "2" in + the List ID field, even if they did not contain "admin" + in the Users Email field.

      +

      - Enter words that will be searched for in all fields in + the "Keyword Search" field.

      +

      - The "Sort By" drop-down menus allow you to select whether + to sort the search results by list ID, Name, description + or creation date, and whether to sort the results in ascending + or descending order.

      +

      Subscribers +
      + Clicking the Subscribers link allows you to search for specific email addresses contained in the lists on your Gossamer List installation.

      Search Subscribers
      + While modifying a list or looking at its subscribers you can click the "Search" link to display a form allowing + you to search for subscribers using any search criteria.

      +

      - Enter a search value in the field provided.

      +

      - Choose to search by email address or subscriber name.

      +

      - Choose which lists you wish to search (or choose all lists) and click the "Search" button.

      +

      -Clicking the "Advanced" link will bring up a more detailed search form, allowing you to search for multiple values and sort your results.

      +

      -You can also do a quick search for subscribers by clicking a letter to view subscribers beginning with that letter.

      +

      While viewing a list of subscribers you can sort the list by clicking on the headings ("Email", "Name", etc.).

      +

      Editing Subscribers
      + While viewing lists of subscribers, you can check the boxes desired subscribers and perform one of three actions on those subscribers by clicking the appropriate button: "Reset Bounced Emails", "Validate Subscribers" or "Delete Subscribers". You can also click on a subscriber's name to bring up a form allowing you to modify that subscriber in more detail.

      +

      -You can choose to mark the subscriber as validated or unvalidated.

      +

      -You can choose to modify the number of times the subscriber's email address has bounced incoming messages.

      +

      -You also have the option of giving the subscriber a name.

      +

      -Click the "Modify a Subscriber" button to confirm changes or click "Reset" to revert to the last saved version of the subscriber. +

      Adding Subscribers
      Clicking the "Add" link brings up a menu allowing you to manually add subscribers one by one to lists. Select the desired list, enter the Email Address and Name of the subscriber in the fields provided and click the "Create Subscriber" button.

      +

      Importing Subscribers
      Clicking the "Import" link will display a form allowing you to import subscribers to a list from a + text file or from a list. See the "Import Subscribers" section above for details.

      + + Remove Bounced Emails
      + While modifying a list or looking at its subscribers you can click the "Remove Bounced" link to remove addresses from the list that have bounced incoming messages a certain number of times. Choose when to remove bounced addresses from the first drop-down menu (equal to a certain number, greater than a certain number, less than or equal to a certain number, etc.), define the number of bounced emails in the text field, and choose which lists to remove the addresses from (either a specific list or all lists). Click the "Search" button. You then have the option of deleting the subscriber records matching your search, or resetting their number of bounced email to zero by checking the desired subscribers and clicking the corresponding button.
      +

      +
      +
      +
      +
      + + diff --git a/site/glist/templates/help/mailings.html b/site/glist/templates/help/mailings.html new file mode 100644 index 0000000..2a11db9 --- /dev/null +++ b/site/glist/templates/help/mailings.html @@ -0,0 +1,176 @@ + + + +Gossamer List - Help - Mailings + + + + + + + + + +
      Close +

      + + + + +
      Help - Mailings
      + + + + +
      + + + + +

      + + + + +
      +
      +

      The Mailings menu allows you to mail and manage the messages + that you've configured to send to specific lists. You can + organize your mailings in folders or search for specific + ones. You can also view and manage your users' mailings + just as you would your own.

      +

      You +are also able to send specific mailings in shell. Pass the following, +replacing "mailing_id" with the numeric ID of the desired mailing:

      +./sendmailing --id=mailing_id

      +

      Whenever you are viewing the Mailings menu, a small folder + menu will be displayed in the top-left corner of the admin + panel.

      +

      - Three folders, "Queued Mailings", "Sent Items" and "Deleted + Items" appear by default. Clicking a folder's name will + display the mailings it contains.

      +

      - +Sub-folders can be created in the "Sent Items" folder by entering a +name in the field provided and selecting in the drop-down menu which +folder the sub-folder should appear in. Click "Add" to create a new +folder. Sent mailings can be moved from folder to folder (within the +main Sent folder). While viewing sent mailings, click the checkboxes +beside the desired mailings, select a destination folder from the +drop-down menu below the list of mailings, and click "Move".

      +

      - Sub-folders can be modified or deleted. Click on the + desired sub-folder's name, change its name in the field + provided and click "Update", or click the "Delete" button.

      +

      Mailings
      + The mailings menu displays records of your mailings, providing + the ID, subject, and number of subscribers who will receive + the mailings. Mailings in the "Queued Mailings" are displayed + by default. If a mailing contains an attachment, this will + be indicated by the paperclip column to the left of the + mailing's Subject. This list can be sorted by ID or by creation + date in ascending or descending order by clicking on the + desired heading. Clicking on a mailing's subject will display + details about that particular mailing, such as the From, + Reply To and Return-path email addresses. The number of + subscribers the mailing will be sent to is also displayed + on this details page; click the number of subscribers to + see a list of their email addresses.
      +
      +- To send mailings to the lists of subscribers they are configured to +be sent to, click the checkboxes beside the desired mailings (click the +checkbox at the top of the menu to select all mailings) and click the +"Start Mailings" button. Note that by default all mailings are +selected; you will have to uncheck the boxes beside mailings you do not +wish to send.

      +

      - To cancel mailings, +click the checkboxes beside the desired mailings (click the checkbox at +the top of the menu to select all mailings) and click the "Cancel +Mailings" button. Note that by default all mailings are selected; you +will have to uncheck the boxes beside mailings you do not wish to +cancel.

      +
      +
      +

      Find Mailings
      + Clicking the "Find Mailing" link will display a form allowing + you to search for specific mailings.

      +

      - Enter values for the mailings you are searching for (Subject, + From Name, etc.), and define your search parameters in the + bottom section of the form.

      +

      - You can define the maximum number of mailings you want + the search to display in the "Maximum Hits" field.

      +

      - If the "Match Any" box is checked the search will return + all mailings containing any of the search criteria entered + in the search form. For example, a "Match Any" search for + mailings with "2" in the Mailing ID field and "admin" in + the Users Email field would return all mailings which contain + "2" in the Mailing ID field, even if they did not contain + "admin" in the Users Email field.

      +

      - Enter words that will be searched for in all fields in + the "Keyword Search" field.

      +

      - The "Sort By" drop-down menus allow you to select whether + to sort the search results by mailing ID or subject date, + and whether to sort the results in ascending or descending + order.

      +

      Check Bounced
      +Clicking the "Check Bounced" link will display a menu allowing you to +login to a remote email account and manage emails sent by Gossamer List +that were bounced. Gossamer Mail will search the POP account you +specify for emails sent by Gossamer Mail and identify any bounced +mails; the email address that bounced the mail will be identified and +the matching subscriber record will be marked as bounced.

      +

      - Enter the remote server name and port entered in the + fields provided.

      +

      - The "Account name" field should contain the same email + address specified in the "Bounce Email" field in your Account + menu. Enter that address' "Password" in the field provided. +

      +

      - +You have the option to automatically "Delete bounced emails". If this +box is checked, all bounced Gossamer List emails will be deleted from +the POP account when you login to it; bounced email addresses will not +be removed from your Gossamer List lists, to do this go to the +"Subscribers" section of the "Lists" menu.

      +

      - +Checking the "Remember Settings" box will save your account +information, so you will be able to instantly login to the same account +the next time you open the "Check Bounced" menu.

      - Click the "Check Bounced Emails" button to view your + bounced emails. These emails can be viewed and managed in + the same way as your regular sent or queued mailings.

      + +

      -Note: +If the number of messages in the POP account exceeds the number defined +in the max_bounced_emails setting in the Misc Setup menu, you should +check the account from shell. Use the following script:
      + + + + + + + + + + + + + + + + + + + + + + + + +

      ./checkbounced.pl --account=username | --host=s --u=s --p=s +[--port=n] | --help

      +
           --host=hostnameThe remote POP3 server name
           --port=110The remote POP3 port. Optional. Defaults to 110
      -u, --username=userPOP3 email account username
      -p, --password=passPOP3 email account password

      -a, --account=username

      GList account username.
      POP3 information will be retrieved from your profile.
      +

      +
      +sendmailing.pl and checkbounced.pl are located at path_to/private/tools +
      +
      +
      + diff --git a/site/glist/templates/help/messages.html b/site/glist/templates/help/messages.html new file mode 100644 index 0000000..e6c0ffe --- /dev/null +++ b/site/glist/templates/help/messages.html @@ -0,0 +1,197 @@ + + + +Gossamer List - Help - Messages + + + + + + + + + + + + +
      Close +

      + + + + +
      Help - Messages
      + + + + +
      + + + + +

      + + + + +
      +
      +

      The Messages menu allows you to create and manage messages + that will be sent to your lists, as well as manage the messages + that have been created by your users. There are three options + within the Messages menu: Messages (displayed by default + when the menu is opened), Create Message, and Find Message. + Additionally, the Folders menu is displayed + at the left at all times while the Messages menu is being + navigated.

      +

      Messages
      + The Messages menu allows you to manage messages that you + have created. A list of all messages is displayed, indicating + the message's subject heading, the date the message was + created and whether or not the message has any attachments + (indicated by the paperclip column to the left of the Subject + heading). This list can be sorted by subject or by creation + date in ascending or descending order by clicking on the + desired heading.
      +
      + - Messages can be deleted by clicking the checkboxes beside + the desired messages (click the checkbox at the top of the + menu to select all messages) and clicking the "Delete Messages" + button.

      +

      - Messages can also be organized into different folders; + see the "Folders" section below for details.

      +

      - Clicking on a message's subject allows you to modify + it (see "Modifying Messages" below).

      +

      Create Message
      + Clicking the Create Message link displays a form allowing + you to define all aspects of a new message.
      +
      + - Note that all fields marked with an asterisk must be filled + in to create the message.

      +

      - Give the message a subject and the name it will appear + as being from when it is delivered.

      +

      - By default, the email address you entered while logging + in will be displayed in the three email address fields. +

      +

      - The "From Email" field should contain the email address + you want the message to be sent from.

      +

      - The "Reply to Email" field should contain the address + you want replies to the message to be sent to.

      +

      - The "Bounce Email" field should contain the address you + want messages that have been bounced to be sent to.

      +

      - Attachments can be added by clicking the "Browse" button, + selecting a file, then clicking the "Upload" button.

      +

      - You have the option of composing the body of the message + in text or HTML. If you select HTML, a plain text version + of the message will be autogenerated for email clients + that do not support HTML. If desired, you can enter the + text portion of the email manually.

      +

      - If you compose an HTML message, you can choose to check two options appearing beneath the message body. You can track the number of list subscribers who open the message. Additionally, if you include a link in the message, you can track the total number of clicks that link receives from all recipients of the message. +

      - You can use any of the following tags to automatically + insert information specific to individual subscribers or + your administrative account:

      +
      +
      +
      +

      <%pro_first_name%> will + automatically insert the value entered in the "First Name" + field in the Account menu.
      + <%pro_last_name%> will automatically insert the + value entered in the "Last Name" field in the Account + menu.
      + <%pro_company%> will automatically insert the value + entered in the "Company Name" field in the Account menu. +
      + <%pro_url%> will automatically insert the value + entered in the "URL" field in the Account menu.
      + <%sub_email%> will automatically insert the subscriber's + email address.
      + <%sub_name%> will automatically insert the subscriber's + name.
      + <%unsubscribe_url%> will automatically insert the URL that the subscriber can click on to be removed from the list If you are creating an HTML message, you could create an unsubscribe link: <a href="<%unsubscribe_url%>">Click here to unsubscribe <%sub_email%></a>.

      +
      +
      +
      +

      - Click the "Create Message" button when you are done or + click "Reset" to reset the form to its default values.

      +

      Modifying Messages
      + Click the desired messages subject, then click the "Edit" link to open the modify message form.

      +

      - Note that all fields marked by an asterisk must be filled + in while modifying messages.

      +

      - New attachments can be added by clicking the "Browse" + button, selecting a file, then clicking the "Upload" button. +

      +

      - Existing attachments can be deleted by checking the boxes + beside the desired attachments and clicking the Delete Attachments + button.

      +

      - Clicking the "Preview" link will display a print preview of the message. From the preview menu you also have the option of sending a copy of the message to an email address (such as one of your own) so you can see how the message will appear when your subscribers receive it. Enter the name and email address of the account you wish to send the message to and click "Send".

      +

      - Clicking the "Select Lists" link will display a + form allowing you to send the message to one of your lists + (see below for details).

      +

      Assigning Messages To Lists
      + After checking the desired message(s), clicking the "Select Lists" link will display a menu allowing you to assign the selected + message(s) to one or more lists.

      +

      - Choose the lists you want the selected message(s) to be + sent to by checking the box beside the desired lists, or + check the box at the top of the form to select all lists. +

      +

      - You can view a list of all of the addresses in a list + by clicking on the desired list's number of subscribers. +

      +

      - Once you've selected the desired lists, click the Queue + Mailing button. Note that this will not actually send the + mailing, but only add it to the "Queued Mailings" folder + in the Mailings menu (which you will automatically be forwarded to), from which it can be sent.

      +

      Find Message
      + Clicking the Find Message link displays a form allowing + you to search for messages using any search criteria.

      +

      - Enter values for the messages you are searching for (Message + ID, Subject, etc.), and define your search parameters in + the bottom section of the form.

      +

      - You can define the maximum number of messages you want + the search to display in the "Maximum Hits" field.

      +

      - If the "Match Any" box is checked the search will return + all messages containing any of the search criteria entered + in the search form. For example, a "Match Any" search for + messages with "update" in the Subject field and "admin" + in the From Name field would return all messages which contain + "update" in the Subject field, even if they did not contain + "admin" in the From Name field.

      +

      - Enter words that will be searched for in all fields in + the "Keyword Search" field. The "Sort By" drop-down menus + allow you to select whether to sort the search results by + message subject or creation date, and whether to sort the + results in ascending or descending order.

      +

      Folders
      + The Folders menu allows you to create folders to organize + your messages in, as well as select which messages you want + to view in the main Messages menu. By default, there is + only one root folder in the Folders menu called "Messages". +

      +

      - New folders can be added by entering a name in the field + provided and clicking "Add". All new folders will appear + as submenus beneath the root "Messages" folder. New folders + cannot appear as submenus of other folders you've created. +

      +

      - Clicking on a folder's name will display all messages + contained within that folder in the main Messages menu. + If you have clicked on a folder you've created, you also + have the option of renaming the folder by changing it's + name in the field provided and clicking "Update", or deleting + the folder by clicking the "Delete" button. Note: Deleting + a folder will also delete all of the messages contained + within it.

      +

      - To move one or more messages to another folder, check + the boxes beside the desired messages, or check the box + at the top of the form to select all messages. Choose a + destination folder from the "Move to" drop-down menu, and + click the "Move" button.

      +

       

      +
      +
      +
      +
      +

       

      + + diff --git a/site/glist/templates/help/plugins.html b/site/glist/templates/help/plugins.html new file mode 100644 index 0000000..48f9716 --- /dev/null +++ b/site/glist/templates/help/plugins.html @@ -0,0 +1,156 @@ + + + +Gossamer List - Help - Plugins + + + + + + + + + + + + +
      Close +

      + + + + +
      Help - Plug-ins
      + + + + +
      + + + + +

      + + + + +
      +
      +

      Plugins are files that can be installed into your copy + of Gossamer List to extend the functionality of many aspects + of the program. The tools in the plugin menu allow you to + browse, download, install, uninstall and modify your plugins, + as well as write your own plugins.

      +

      Warning: Plugins contain + code that will be executed on and affect your system. Be + careful while installing any plugins from a source of which + you are unsure or do not trust.

      +

      Plugin Manager
      + The Plugin Manager (which will be displayed by default when + you view the Plugins menu) allows you to easily install, + edit, uninstall and delete plugins that you have downloaded, + and displays brief descriptions of those plugins. The plugin + manager is divided into two menus, the "Installed Plugins" + menu and the "Uninstalled Plugins" menu. Each plugin that + you have downloaded is listed in one of these two menus + (which menu each plugin is listed in is obviously dependant + on whether or not the plugin is currently installed). To + the right of the name of each plugin, the version number + and author of the plugin are listed. The author's name may + be linked to their web site or email address. To the right + of the author's name you are given the options of installing, + editing, or deleting the plugin.

      +

      To install a plugin, click the "Install" + link:

      +

      - A screen describing the plugin and how to use it will + be displayed.

      +

      - Click the "Install" button at the bottom of the screen + to confirm the installation.

      +

      - If you are installing a newer version of a plugin already + on your system, you will be given a prompt asking if you + want to overwrite the pre-existing plugin with the newer + version.

      +

      To edit a plugin, click the "Edit" link: +

      +

      - A screen displaying the plugins options will appear. +

      +

      - Make any changes that you want, and click the "Edit" + button to confirm them.

      +

      To uninstall a plugin, click the "Uninstall" + link:

      +

      - A screen either describing the plugin or describing how + the uninstall will affect Gossamer List will be displayed. +

      +

      - You can either completely uninstall the plugin and all + of its files and records by clicking the "Full Uninstall" + button, or only uninstall the plugin itself by clicking + "Skip Plugin Uninstall". This option will leave any files + or records created by the plugin untouched, and is useful + if you are planning to reinstall the plugin at some point. +

      +

      To delete a plugin, click the "Delete" + link:

      +

      - You will be given the option of confirming or aborting + the delete.

      +

      - A plugin must be uninstalled before it can be deleted. +

      +

      Plugin Download
      + The Plugin Download menu allows you to download and install + plugins in three ways: from the Gossamer Threads website, + from another URL, or from a file.

      +

      To download plugins from Gossamer Threads:

      +

      - Click the "Show" button to see a list of plugins available + for download direct from Gossamer Threads. If you have already + installed any of these plugins, the version number of your + copy of the plugin will be displayed next to the version + number of the latest available version of the plugin, so + you can easily identify plugins that have been updated since + you last installed them. Note: You will not be able to download + plugins from Gossamer Threads if you do not have a valid + registration number entered in the "Misc Options" section + of the Setup menu.

      +

      - Click the "Download" link beside a plugin to begin downloading + it.

      +

      To install plugins from a URL:

      +

      - Enter the URL location of the plugin you wish to download + in the field provided.

      +

      - Click the "Install" button.

      +

      To Install plugins from a file:

      +

      - Enter the file location of the plugin you wish to download + in the field provided, or click the "Browse" button to search + for the file.

      +

      - Click the "Install" button when the correct file location + has been entered.

      +

      Plugin Wizard
      + The Plugin Wizard tool allows you to create your own plugins, + or modify pre-existing ones.

      +

      If you are creating a new plugin, enter its name in the + "Create new plugin named:" field and click the "Next" button. + If you are modifying a pre-existing plugin, select it from + the drop-down menu and click the "Next" button.

      +

      - The first plugin wizard menu allows you to specify basic + information about your plugin: version number, author, licensing + (freeware, shareware, commercial, etc), Gossamer List version + requirements, and description.

      +

      - The second plugin wizard menu allows you to create hooks + for the plugin.

      +

      - The third plugin wizard menu allows you to specify menu + options.

      +

      - The fourth plugin wizard menu allows you to specify user + options.

      +

      - If your plugin requires bundled files, the fifth plugin + wizard menu allows you to configure those files.

      +

      - The sixth plugin wizard menu allows you to configure + the messages that will be displayed and the code that will + be executed when the plugin is installed and uninstalled +

      +

      About Plugins
      + Clicking on "About Plugins" will give you a brief description + of plugins and how they can be used in Gossamer List.

      +

      +
      +
      +
      + + diff --git a/site/glist/templates/help/reports.html b/site/glist/templates/help/reports.html new file mode 100644 index 0000000..67839c6 --- /dev/null +++ b/site/glist/templates/help/reports.html @@ -0,0 +1,67 @@ + + + +Gossamer List - Help - Reports + + + + + + + + + + + + +
      Close +

      + + + + +
      Help - Reports
      + + + + +
      + + + + +

      + + + + +
      +
      +

      The "Reports" menu allows you to get information on the + mailings performed by yourself and (if you are an administrator) + your users. Opening the "Reports" menu will display a list + of administrators and users and the number of mailings they have each + performed in the current month.

      +

      Clicking on the the username of a specific administrator + or user will display detailed records of that user's mailings, + containing subjects, the dates on which the mailings were + sent and the number of recipients of each mailing (you can click on the number of recipients to view a list of them).

      +

      To view reports on specific administrators or users (if + you are an administrator) or view mailings from a period + other than the current month, enter the desired account + email in the "Username" field. Select a month from the first + drop-down menu and a year from the second drop-down + menu, and click "Build Report".

      +

      Clicking the "Advanced" link will display a menu allowing + you to further customize a report. Enter an account (if + you are an administrator), provide the range of dates from + which the report will be built, and specify the number of + mailings per page you wish to view in the report in the + "Max Hits" field.

      +
      +
      +
      +
      +
      + + diff --git a/site/glist/templates/help/setup.html b/site/glist/templates/help/setup.html new file mode 100644 index 0000000..c4a7275 --- /dev/null +++ b/site/glist/templates/help/setup.html @@ -0,0 +1,165 @@ + + + +Gossamer List - Help - Setup + + + + + + + + + + + + +
      Close +

      + + + + +
      Help - Setup
      + + + + +
      + + + + +

      + + + + + + + +
      +
      +

      The Setup menu allows you to configure Gossamer List's + options and preferences: server and email settings, file + locations and user permissions are all controlled from the + Setup menu.

      +

      SQL Server
      + The SQL Server menu allows you to designate the type of + SQL server Gossamer List will use. The SQL setup information + you provided while installing Gossamer List will appear + here.

      +

      If you make any changes to your SQL server, Gossamer List + will have to rebuild your database. Select one of the following + three options for the rebuild of the new database:

      +
      +
      +
      +

      - Create new default Gossamer List tables in this database + but do not overwrite any existing data.

      +

      - Create new default Gossamer List tables in this database + and overwrite/erase any existing data.

      +

      - Load table info from an existing set of tables (or + resync the def files if you have changed your defs manually). +

      +
      +
      +
      +

      Click the "Submit Changes" button when you have finished + configuring the server options.

      +

      Paths and URLs
      + The options in the Paths and URLs menu allow you to view + and set the locations of your Gossamer List files and scripts. + A path is the location of a file or group of files on a + hard drive, while a URL is the location of a file or group + of files on a website. Most of the default settings of these + paths and URLs will be correct and will not require changing. + Be sure to include "http://" at the beginning of all URLs, + and do not include a trailing slash at the end of the URLs + and paths. All paths should begin with either a forward + slash (/), or a drive letter (c:). Click the "Submit Changes" + button to confirm your changes. Note: Windows users should + use drive letters at the beginning of all paths.

      +

      - priv_path: This field should contain the path to your + private Gossamer List files, such as batch (incoming, outgoing + and bounce) and template files. This location should not + be web accessible. Do not use a trailing slash.

      +

      - cgi _url: This field should contain the URL where your + public cgi scripts are located is located.

      +

      - image_url: This field should contain the URL where all + public images used in Gossamer List. It can either be relative + (/pics) or absolute (http://server/pics).

      + +

      Misc Options

      +

      - template_set: Choose the template you wish to use.

      +

      - template_backups: Choose whether or not to create backup template files. If this option is set to yes, each time you save your template a backup file containing the previous version will be saved as well.

      +

      - reg_number: This field should contain your Gossamer List + registration number. You will not be able to download plugins + from Gossamer Threads if you do not have a valid registration + number entered here.

      +

      - smptp_server: If you are on a Windows server, this field + should contain the name of the smtp server that will be + used to send email. Either this field or the mail_path option + (but only one of them) must be configured.

      +

      - mail_path: If you are on UNIX, this field should contain + the path to the "sendmail" program. Either this field or + the smtp_server option (but only one of them) must be configured. +

      +

      - debug_level: This option allows you to enable and disable debug mode. + Note: Debug mode should only be enabled when you are doing + system maintenance. Debug mode creates significant amounts + of overhead, adds large amounts of debug code to the error + log and may allow users to obtain access to the server. + Make sure to set it back to "disabled" when finished. +

      +

      - user_sessions: Choose whether or not to generate a session_id. + If this is set to "No", users will have to configure their + browsers to allow cookies to view the database.

      +

      - session_exp: This field contains the amount of time (in + hours) that session_ids will be valid for.

      +

      - highlight_colour: Choose whether or not keyword search terms will be highlighted while searching for mailings and messages.

      +

      - max_attachments_size: This field contains the maximum + size (in bytes) of attachments that will be permitted to + be attached to mailings.

      +

      - max_bounced_emails: This field allows you to set the maximum number of bounced messages you will be able to retrieve from the Check Bounced menu. If there are more messags, you should run the shell script outlined in the Mailings help.

      +

      -error_message: You can enter a custom error handler in + this field. Gossamer List will display the HTML entered + in this field in any error message that is generated. If + left blank, the default error message will be shown. You + can only use two template tags in this: <%error%> + for the error message, and <%environment%> for a complete + debug message.

      +

      - html_code: This field contains the default HTML that + will be used to generate the "subscribe to list" forms created + in the Lists menu.

      + +

      User Signup

      +

      - signup_enable: This option determines whether or not new users will be able to signup for their own Gossamer List accounts.

      +

      - signup_restricted_email: This field allows you to define email addresses that new users will not be able to sign up with. Enter addresses one per line.

      +

      - signup_email_validate: If this option is enabled, new users will be sent a validation code via email which must be acknowledged in order for the new account to be validated.

      +

      - signup_admin_validate: If this option is enabled, new user accounts will have to be validated by an administrator from the "User Management" section of the Account menu.

      +

      - signup_username_regex: This option allows you to define a regular expression that new users' email addresses will have to match in order to ensure valid email addresses. If you are not familiar with regular expressions, do not modify this field.

      +

      - signup_limit_list: This option allows you to define the maximum number of lists users will be allowed to have. +

      +

      - signup_limit_sublist: This option allows you to define the maximum number of subscribers per list users will be allowed to have.

      +

      - signup_limit_email30: This option allows you to define the maximum number of emails users will be allowed to send from their Gossamer List account in a thirty day period.

      + +

      Stop List

      +

      Clicking the "Stop List" link shows a menu allowing you to create and manage a list of subscribers + that will be prevented from receiving mailings from you. You can navigate + the list of addresses on the stop list just as you would the regular Subscribers menu. Addresses + can be removed from the stop list by checking the boxes beside the desired addresses and clicking + the "Delete Emails" button. To add addresses to the stop list, click the "Add Emails" button and + enter the addresses in the field that appears. Enter emails one per line. Click the "Add Emails" + button to confirm the new addresses.

      +

      Including a ? in an email address will substitute any single character, while including a * will substitute any combination of any number of characters (including zero). For example, adding "999?@foo.com" to the stop list would prevent "9990@foo.com", "9991@foo.com", "9992@foo.com", etc. from receiving mail. Adding "*@foo.com" would stop "bob@foo.com", "1234@foo.com", "spam@foo.com", etc. from receiving mail.

      + +

      Global Header & Footer

      + The Global Header & Footer menu allows you to create headers and footers that will be attached to the tops and bottoms of all messages sent by users from your Gossamer List installation (these headers and footers will be attached in addition to any personal headers and footers users may have created in their Account menu). Whether the HTML or text headers and footers are attached is dependant on whether the message being sent is in HTML or text format. + +
      +
       
      +
      +
      + + diff --git a/site/glist/templates/help/templates.html b/site/glist/templates/help/templates.html new file mode 100644 index 0000000..2657800 --- /dev/null +++ b/site/glist/templates/help/templates.html @@ -0,0 +1,142 @@ + + + +Gossamer List - Help - Templates + + + + + + + + + + + + +
      Close +

      + + + + +
      Help - Templates
      + + + + +
      + + + + +

      + + + + +
      +

      The Templates menu allows you to manage all of the templates + used in Gossamer List: the "Templates" that build the pages + users see while using Gossamer List as well as the "Email + Templates" used to build the emails that are automatically + sent to subscribers. The "Language" menu allows you to edit + the language that Gossamer List will display in prompts, warnings + and error messages. The "Template Globals" menu allows you + to add and edit globals: HTML tags that can be used in all + templates.

      +
      +

      Templates
      + Clicking "Templates" allows you to edit the templates that + create Gossamer List's HTML pages. By altering these templates, + you can customize the appearances and functions of the HTML + pages through which you and your users interact with Gossamer + List.

      +

      - Select a specific template from the drop-down menus. +

      +

      - Click "Load" to see the template displayed in the main + text area in the centre of the page.

      +

      - Text wrapping can be toggled on and off by clicking the "Enable/Disable Wrap" button.

      +

      - The size of the main text area can be edited (enter values + for the columns and rows in the "Resize" fields and click + the "Resize" button). Text wrapping can also be toggled + on and off.

      +

      - Insert changes to the template in HTML in the + main text area.

      +

      - You can automatically go to a certain line in the template + by entering the desired line number in the "Line" field + and clicking the "Go" button.

      +

      - Click "Save" to confirm your changes to the template + (you can alter the name of the template in the field beside + the "Save" button).

      +

      Default Email Templates
      + Clicking the "Default Email Templates" link brings up a menu allowing you toedit the templates that will build the default subscription, unsubscriptionand validation emails that are sent to your subscribers and your users'subscribers. Note that these templates only determine the initial content ofthese templates for new users; you and your users can modify your ownpersonal email templates from the "Email Templates" section of the Accountmenu. +

      +

      - Select the specific template you wish to modify from + the drop-down menu and click "Load". The email template + will be displayed in the "To", "Subject", "From", "Extra + Body" and "E-mail body" fields.

      +

      - Make changes to the text and tags included in the desired + fields and click the "Save" button. The following tags can + be included in any of the fields:

      +
      +
      +
      +

      <%pro_first_name%> will automatically insert the + value entered in the "Full Name" field in the Account + menu.
      + <%pro_last_name%> will automatically insert the + value entered in the "Full Name" field in the Account + menu.
      + <%usr_email%> will automatically insert the value + entered in the "Email" field in the Account menu.
      + <%pro_url%> will automatically insert the value + entered in the "URL" field in the Account menu.
      + <%sub_email%> will automatically insert the subscriber's + email address.
      + <%sub_name%> will automatically insert the subscriber's + name.

      +
      +
      +
      +

      You can save the template under a different name by entering + one in the "Save template as:" field and clicking the "Save" + button.

      +

      Language
      + Clicking "Language" displays a menu allowing you to view + and modify the text that will be displayed in prompts, warnings + and error messages.

      +

      - Click on a language category (ADD, LOG, SYS, etc.) to + bring up a list of all messages in that category.

      +

      - Re-type the message in the "Description" field in the + language you want, and click the "Save Changes" button. + Changes to the messages will be applied throughout Gossamer + List wherever the corresponding language code (displayed + to the left of the "Description" field) is found.

      +

      - You can also add or delete codes and descriptions in + the User Language menu. Enter new codes and descriptions + in the blank fields at the bottom of the page, or check + the "Delete" boxes beside the messages and click the "Save + Changes" button. The titles of new codes will have to begin with the category they belong to and an underscore: "ADD_newcode", for example.

      +

      Template Globals
      + Clicking "Template Globals" allows you to edit Gossamer + List's globals. Globals are tags that call a variable (text, + HTML, etc.) that can be used in all templates. By editing + and adding globals, you can configure frequently used tags + to be quickly and easily inserted into any template you + wish.

      +

      - To edit an existing global, make changes to the content + in its "Description" field, and click the "Save Changes" + button.

      +

      - To create a new global, enter a name for it in the blank + "Code" field at the bottom of the menu. Enter the new global's + content in the blank "Description" field at the bottom of + the menu (globals can be written in HTML or regular text), + and click the "Save Changes" button.

      +

       

      +
      +
      +
      +
      + + diff --git a/site/glist/templates/help/users.html b/site/glist/templates/help/users.html new file mode 100644 index 0000000..0958e29 --- /dev/null +++ b/site/glist/templates/help/users.html @@ -0,0 +1,181 @@ + + + +Gossamer List - Help - Users + + + + + + + + + + + + +
      Close +

      + + + + +
      Help - User
      + + + + +
      + + + + +

      + + + + +
      +
      + +

      User Management
      + Clicking the "User Management" link will display a list + of all of your Gossamer List users, and allow you to create, + modify and delete user records.

      +

      Adding A User
      + Clicking the "Add a User" button will display a form allowing + your to create and customize a new user record.

      +

      - The values entered in the "Full Name", "Company Name" + and "URL" fields can be automatically added to messages + sent from Gossamer List using the following tags:

      +
      +
      +
      +

      <%pro_first_name%>
      + <%pro_last_name%>
      + <%usr_email%>
      + <%pro_company%>
      + <%pro_url%>

      +
      +
      +
      +

      - Choose whether the user record will be "active" or "inactive" + in the drop-down menu. A user with an inactive record will + not be able to log in, and their mailings will not be sent. +

      +

      The "Reply-to-Email" and "Bounce Email" fields contain + the addresses that will be set as the default reply and + bounce email addresses when the user is creating a new message. +

      +

      The "Reply to Email" field should contain the address you + want replies to the message to be sent to.

      +

      The "Bounce Email" field should contain the address you + want messages that have been bounced to be sent to.

      +

      The "Date format" drop-down menu controls the date format + that will be used for the user's messages. To create a custom date format, select one from the drop-down menu and modify the format in the field to the right.

      +

      The "Editor Advanced" field allows you to choose the default message composition format for the user (Text, HTML or both).

      + +

      - Click the "Update Profile" button to save changes, or + click the "Reset" button to blank the form.

      +

      Editing User Records
      + Clicking the "edit" link beside a specific user record will + display a menu allowing you to edit all aspects of that + record.

      +

      - The "User Email" will be the address provided by you + or the user when the user record was created. This address + can not be changed.

      +

      - The values entered in the "First Name", "Last Name" "Company + Name", "Email" and "URL" fields can be automatically added + to messages sent from Gossamer List using the following + tags:

      +
      +
      +
      +

      <%pro_first_name%>
      + <%pro_last_name%>
      + <%usr_email%>
      + <%pro_company%>
      + <%pro_url%>

      +
      +
      +
      +

      - Choose whether the user record will be "active" or "deactive" + in the drop-down menu. A user with a deactive record will + not be able to log in, and their mailings will not be sent. +

      +

      - The "Reply-to-Email" and "Bounce Email" fields contain + the addresses that will be set as the default reply and + bounce email addresses when the user is creating a new message. +

      +

      - The "Date format" drop-down menu controls the date format + that will be used for the user's messages.

      +

      - Click the "Update + Profile" button to save changes, or click the "Reset" button + to restore the original values.

      +

      Deleting A User
      + To delete users, click the checkboxes beside the desired + user records (click the checkbox at the top of the menu + to select all mailings), and click the "Delete Users" button. +

      +

      Searching For Users
      + Clicking the "Search Users" link will display a detailed + search form allowing you to search for records by any criteria.

      +

      - Enter values for the users you are searching for (Email, + First Name, etc.), and define your search parameters in + the bottom section of the form.

      +

      - You can define the maximum number of users you want the + search to display in the "Maximum Hits" field.

      +

      - If the "Match Any" box is checked the search will return + all users containing any of the search criteria entered + in the search form. For example, a "Match Any" search for + mailings with "user" in the Email field and "Gossamer" in + the Company field would return all users which contain "user" + in the Email field, even if they did not contain "Gossamer" + in the Company field.

      +

      - The "Sort By" drop-down menus allow you to select whether + to sort the search results by Email, First Name, Last Name, + Reply-to Email and User Type, and whether to sort the results + in ascending or descending order.

      +

      - Click the "Search" button to search for users, or click + the "Reset" button to blank the form.

      + +

      Customize Profile
      + The "Customize Profile" menu allows you to view and modify the columns that are used in the "Profile" section of users' accounts. Click on the "Add a Field" button to create a new column, or click on an existing field name to modify a column. The following fields can be defined while creating/modifying columns:

      +

      Column Name: This is simply the name of the column that will appear in the list of columns and in the templates. The column name must be entered in valid SQL format (no spaces between words, etc.).

      +

      Column Type: This is the type of data that will be stored in the column. You are given a drop-down menu with the following data types to choose from:
      +

      + INT: An integer column contains a positive or negative number with no decimals. The "ID" and "Hits" columns are examples of integer columns.
      + TINYINT:An integer ranging from -128 to 127. A TINYINT column will occupy less space than a regular INT column.
      + CHAR: A character column contains a string of no more that 255 characters.
      + VARCHAR: A variable character column contains a string of characters of unlimited length. The "Name" column is an example of a variable character column.
      + TEXT: A text column contains a string of characters of any length. The "Description" and "Meta_Keywords" columns are examples of text columns.
      + DATE: A date column contains a date that complies with the "Date Options" in the Setup menu. The "Add Date" and "Mod Date" column are examples of date columns.
      + DATETIME: A datetime column stores both a date and a specific time of day in the format: YYY-MM-DD hh-mm:ss

      + +

      Column Size: If the column is a VARCHAR or CHAR type, you can enter the maximum number of characters the column will contain here.

      +

      Not Null: If this is checked "yes", then the column must be given a value when a record is being created or modified. If this option is checked "no", no value is necessary.

      +

      Default: If you want the column to have a default value that will appear when a record is being created, enter it here. If the column has a form type that only permits the selection of pre-designated options (SELECT, MULTI-SELECT, CHECKBOX, or RADIO), enter one of those pre-designated options in the "Default" field to make the field default to it when a record is being created.

      +

      Form Display: If you want a name other than the actual Column Name to appear when records are being displayed or edited, enter it here. This name does not have to be entered in valid SQL format, so a column with a Column Name of "extended_description" can appear as "Extended Description".

      +

      Form Type: This option sets the appearance the column will have when it is displayed. You are given a drop-down menu with the following form types to choose from:
      +

      + HIDDEN: The column will not be displayed when records are being viewed, added or modified.
      + SELECT: A vertical list of pre-designated options of which only one can be selected:
      + MULTI-SELECT: A vertical list of pre-designated options of which any number can be selected. Hold down the CTRL button while clicking in order to select more than one option.
      + CHECKBOX: A series of checkboxes allowing the selection of all, some, or none of a group of pre-designated options.
      + RADIO: A series of checkboxes allowing the selection of only one of a group of pre-selected options.
      + TEXT: A single-line text field.
      + TEXTAREA: A scrollable text field.
      + PASSWORD: A field which will have all input typed into it masked by asterisks.

      +

      Form Size: This is the size of the form of the column. The number that is entered here is equivalent to the number of lines that the form will have if it is a SELECT or MULTI-SELECT columns, and is roughly equivalent to the number of character spaces which will fit in one line of the form if it is a TEXT or TEXTAREA column. The Form Size field has no effect on HIDDEN, CHECKBOX and RADIO columns.

      +

      Form Names: If the column is a SELECT, MULTI-SELECT, CHECKBOX or RADIO type, enter the values of the pre-designated options that you want to be stored in the database. The Form Names field has no effect on HIDDEN, TEXT, and TEXTAREA columns.

      +

      Form Values: If the column is a SELECT, MULTI-SELECT, CHECKBOX or RADIO type, enter the names of the pre-designated options that you want to display to the user while adding or modifying a record. The Form Values field has no effect on HIDDEN, TEXT, and TEXTAREA columns.

      +

      Form Regex: The Regex (regular expression) is the format (as written in perl) with which all input in a field must comply. If the value in the column does not comply with the Form Regex, it will not be accepted.

      + + +

       

      +
      +
      +
      +
      + + diff --git a/site/glist/templates/safe/admin_template_home.html b/site/glist/templates/safe/admin_template_home.html new file mode 100644 index 0000000..a7967e8 --- /dev/null +++ b/site/glist/templates/safe/admin_template_home.html @@ -0,0 +1,47 @@ +<%set set_focus = 'myform.tpl_dir'%> +<%set book_mark = 'template'%> +
      +<%include include_style.txt%> + + + + +
      + + + + + + + +
      + + + + + + + +
      + + + + +
      + + + + + +
      Edit Templates
      +
      +
      + <%include admin_template_include_record.html%> +
      +
      +
      + + + +<%hidden_objects%> +
      diff --git a/site/glist/templates/safe/admin_template_include_record.html b/site/glist/templates/safe/admin_template_include_record.html new file mode 100644 index 0000000..e600cdd --- /dev/null +++ b/site/glist/templates/safe/admin_template_include_record.html @@ -0,0 +1,160 @@ + + +<%GList::Tools::template_editor%> +<%if readme%>

      <%readme%>

      <%endif%> + + + + + + + + + + + + + + <%if tpl_name%> + + + + + <%endif%> + + + + + + + + + + +

      + <%if message%><%message%><%else%>From here you can quickly edit any of your user or admin templates.<%endif%>

      +
      Template Set: + <%dir_select%> + +
      Available templates:<%file_select%> +
      + (* denotes a modified or non-standard template) +
      Currently working on: + <%tpl_name%> + <%if file_local%>value="Restore" name="restore=<%tpl_name%>"<%else%>value="Delete" name="delete=<%tpl_name%>"<%endif%> /><%endif%> +
      Save template as: + + +
      + <%if is_ie or is_mozilla%> +

      + <%else%> +

      + <%endif%> +
      + + + + + +
      + <%if is_ie or is_mozilla%> + + + rows + cols + <%else%> + + rows + cols + <%endif%> + + <%if is_ie%> + + <%endif%> +
      +
      +
      + diff --git a/site/googlemaps/roadshow_map.html b/site/googlemaps/roadshow_map.html new file mode 100644 index 0000000..1ea5448 --- /dev/null +++ b/site/googlemaps/roadshow_map.html @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + +
      + + + diff --git a/site/racecalendar/extlib/Snoopy.class.inc b/site/racecalendar/extlib/Snoopy.class.inc new file mode 100644 index 0000000..22dc434 --- /dev/null +++ b/site/racecalendar/extlib/Snoopy.class.inc @@ -0,0 +1,901 @@ + +Copyright (c): 1999-2000 ispi, all rights reserved +Version: 1.0 + + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +You may contact the author of Snoopy by e-mail at: +monte@ispi.net + +Or, write to: +Monte Ohrt +CTO, ispi +237 S. 70th suite 220 +Lincoln, NE 68510 + +The latest version of Snoopy can be obtained from: +http://snoopy.sourceforge.com + +*************************************************/ + +class Snoopy +{ + /**** Public variables ****/ + + /* user definable vars */ + + var $host = "www.php.net"; // host name we are connecting to + var $port = 80; // port we are connecting to + var $proxy_host = ""; // proxy host to use + var $proxy_port = ""; // proxy port to use + var $agent = "Snoopy v1.0"; // agent we masquerade as + var $referer = ""; // referer info to pass + var $cookies = array(); // array of cookies to pass + // $cookies["username"]="joe"; + var $rawheaders = array(); // array of raw headers to send + // $rawheaders["Content-type"]="text/html"; + + var $maxredirs = 5; // http redirection depth maximum. 0 = disallow + var $lastredirectaddr = ""; // contains address of last redirected address + var $offsiteok = true; // allows redirection off-site + var $maxframes = 0; // frame content depth maximum. 0 = disallow + var $expandlinks = true; // expand links to fully qualified URLs. + // this only applies to fetchlinks() + // or submitlinks() + var $passcookies = true; // pass set cookies back through redirects + // NOTE: this currently does not respect + // dates, domains or paths. + + var $user = ""; // user for http authentication + var $pass = ""; // password for http authentication + + // http accept types + var $accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*"; + + var $results = ""; // where the content is put + + var $error = ""; // error messages sent here + var $response_code = ""; // response code returned from server + var $headers = array(); // headers returned from server sent here + var $maxlength = 500000; // max return data length (body) + var $read_timeout = 0; // timeout on read operations, in seconds + // supported only since PHP 4 Beta 4 + // set to 0 to disallow timeouts + var $timed_out = false; // if a read operation timed out + var $status = 0; // http request status + + var $curl_path = "/usr/bin/curl"; + // Snoopy will use cURL for fetching + // SSL content if a full system path to + // the cURL binary is supplied here. + // set to false if you do not have + // cURL installed. See http://curl.haxx.se + // for details on installing cURL. + // Snoopy does *not* use the cURL + // library functions built into php, + // as these functions are not stable + // as of this Snoopy release. + + // send Accept-encoding: gzip? + var $use_gzip = true; + + /**** Private variables ****/ + + var $_maxlinelen = 4096; // max line length (headers) + + var $_httpmethod = "GET"; // default http request method + var $_httpversion = "HTTP/1.0"; // default http request version + var $_submit_method = "POST"; // default submit method + var $_submit_type = "application/x-www-form-urlencoded"; // default submit type + var $_mime_boundary = ""; // MIME boundary for multipart/form-data submit type + var $_redirectaddr = false; // will be set if page fetched is a redirect + var $_redirectdepth = 0; // increments on an http redirect + var $_frameurls = array(); // frame src urls + var $_framedepth = 0; // increments on frame depth + + var $_isproxy = false; // set if using a proxy server + var $_fp_timeout = 30; // timeout for socket connection + +/*======================================================================*\ + Function: fetch + Purpose: fetch the contents of a web page + (and possibly other protocols in the + future like ftp, nntp, gopher, etc.) + Input: $URI the location of the page to fetch + Output: $this->results the output text from the fetch +\*======================================================================*/ + + function fetch($URI) + { + + //preg_match("|^([^:]+)://([^:/]+)(:[\d]+)*(.*)|",$URI,$URI_PARTS); + $URI_PARTS = parse_url($URI); + if (!empty($URI_PARTS["user"])) + $this->user = $URI_PARTS["user"]; + if (!empty($URI_PARTS["pass"])) + $this->pass = $URI_PARTS["pass"]; + + switch($URI_PARTS["scheme"]) + { + case "http": + $this->host = $URI_PARTS["host"]; + if(!empty($URI_PARTS["port"])) + $this->port = $URI_PARTS["port"]; + if($this->_connect($fp)) + { + if($this->_isproxy) + { + // using proxy, send entire URI + $this->_httprequest($URI,$fp,$URI,$this->_httpmethod); + } + else + { + $path = $URI_PARTS["path"].(isset($URI_PARTS["query"]) ? "?".$URI_PARTS["query"] : ""); + // no proxy, send only the path + $this->_httprequest($path, $fp, $URI, $this->_httpmethod); + } + + $this->_disconnect($fp); + + if($this->_redirectaddr) + { + /* url was redirected, check if we've hit the max depth */ + if($this->maxredirs > $this->_redirectdepth) + { + // only follow redirect if it's on this site, or offsiteok is true + if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok) + { + /* follow the redirect */ + $this->_redirectdepth++; + $this->lastredirectaddr=$this->_redirectaddr; + $this->fetch($this->_redirectaddr); + } + } + } + + if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0) + { + $frameurls = $this->_frameurls; + $this->_frameurls = array(); + + while(list(,$frameurl) = each($frameurls)) + { + if($this->_framedepth < $this->maxframes) + { + $this->fetch($frameurl); + $this->_framedepth++; + } + else + break; + } + } + } + else + { + return false; + } + return true; + break; + case "https": + if(!$this->curl_path || (!is_executable($this->curl_path))) { + $this->error = "Bad curl ($this->curl_path), can't fetch HTTPS \n"; + return false; + } + $this->host = $URI_PARTS["host"]; + if(!empty($URI_PARTS["port"])) + $this->port = $URI_PARTS["port"]; + if($this->_isproxy) + { + // using proxy, send entire URI + $this->_httpsrequest($URI,$URI,$this->_httpmethod); + } + else + { + $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : ""); + // no proxy, send only the path + $this->_httpsrequest($path, $URI, $this->_httpmethod); + } + + if($this->_redirectaddr) + { + /* url was redirected, check if we've hit the max depth */ + if($this->maxredirs > $this->_redirectdepth) + { + // only follow redirect if it's on this site, or offsiteok is true + if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok) + { + /* follow the redirect */ + $this->_redirectdepth++; + $this->lastredirectaddr=$this->_redirectaddr; + $this->fetch($this->_redirectaddr); + } + } + } + + if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0) + { + $frameurls = $this->_frameurls; + $this->_frameurls = array(); + + while(list(,$frameurl) = each($frameurls)) + { + if($this->_framedepth < $this->maxframes) + { + $this->fetch($frameurl); + $this->_framedepth++; + } + else + break; + } + } + return true; + break; + default: + // not a valid protocol + $this->error = 'Invalid protocol "'.$URI_PARTS["scheme"].'"\n'; + return false; + break; + } + return true; + } + + + +/*======================================================================*\ + Private functions +\*======================================================================*/ + + +/*======================================================================*\ + Function: _striplinks + Purpose: strip the hyperlinks from an html document + Input: $document document to strip. + Output: $match an array of the links +\*======================================================================*/ + + function _striplinks($document) + { + preg_match_all("'<\s*a\s+.*href\s*=\s* # find ]+)) # if quote found, match up to next matching + # quote, otherwise match up to next space + 'isx",$document,$links); + + + // catenate the non-empty matches from the conditional subpattern + + while(list($key,$val) = each($links[2])) + { + if(!empty($val)) + $match[] = $val; + } + + while(list($key,$val) = each($links[3])) + { + if(!empty($val)) + $match[] = $val; + } + + // return the links + return $match; + } + +/*======================================================================*\ + Function: _stripform + Purpose: strip the form elements from an html document + Input: $document document to strip. + Output: $match an array of the links +\*======================================================================*/ + + function _stripform($document) + { + preg_match_all("'<\/?(FORM|INPUT|SELECT|TEXTAREA|(OPTION))[^<>]*>(?(2)(.*(?=<\/?(option|select)[^<>]*>[\r\n]*)|(?=[\r\n]*))|(?=[\r\n]*))'Usi",$document,$elements); + + // catenate the matches + $match = implode("\r\n",$elements[0]); + + // return the links + return $match; + } + + + +/*======================================================================*\ + Function: _striptext + Purpose: strip the text from an html document + Input: $document document to strip. + Output: $text the resulting text +\*======================================================================*/ + + function _striptext($document) + { + + // I didn't use preg eval (//e) since that is only available in PHP 4.0. + // so, list your entities one by one here. I included some of the + // more common ones. + + $search = array("']*?>.*?'si", // strip out javascript + "'<[\/\!]*?[^<>]*?>'si", // strip out html tags + "'([\r\n])[\s]+'", // strip out white space + "'&(quote|#34);'i", // replace html entities + "'&(amp|#38);'i", + "'&(lt|#60);'i", + "'&(gt|#62);'i", + "'&(nbsp|#160);'i", + "'&(iexcl|#161);'i", + "'&(cent|#162);'i", + "'&(pound|#163);'i", + "'&(copy|#169);'i" + ); + $replace = array( "", + "", + "\\1", + "\"", + "&", + "<", + ">", + " ", + chr(161), + chr(162), + chr(163), + chr(169)); + + $text = preg_replace($search,$replace,$document); + + return $text; + } + +/*======================================================================*\ + Function: _expandlinks + Purpose: expand each link into a fully qualified URL + Input: $links the links to qualify + $URI the full URI to get the base from + Output: $expandedLinks the expanded links +\*======================================================================*/ + + function _expandlinks($links,$URI) + { + + preg_match("/^[^\?]+/",$URI,$match); + + $match = preg_replace("|/[^\/\.]+\.[^\/\.]+$|","",$match[0]); + + $search = array( "|^http://".preg_quote($this->host)."|i", + "|^(?!http://)(\/)?(?!mailto:)|i", + "|/\./|", + "|/[^\/]+/\.\./|" + ); + + $replace = array( "", + $match."/", + "/", + "/" + ); + + $expandedLinks = preg_replace($search,$replace,$links); + + return $expandedLinks; + } + +/*======================================================================*\ + Function: _httprequest + Purpose: go get the http data from the server + Input: $url the url to fetch + $fp the current open file pointer + $URI the full URI + $body body contents to send if any (POST) + Output: +\*======================================================================*/ + + function _httprequest($url,$fp,$URI,$http_method,$content_type="",$body="") + { + if($this->passcookies && $this->_redirectaddr) + $this->setcookies(); + + $URI_PARTS = parse_url($URI); + if(empty($url)) + $url = "/"; + $headers = $http_method." ".$url." ".$this->_httpversion."\r\n"; + if(!empty($this->agent)) + $headers .= "User-Agent: ".$this->agent."\r\n"; + if(!empty($this->host) && !isset($this->rawheaders['Host'])) + $headers .= "Host: ".$this->host."\r\n"; + if(!empty($this->accept)) + $headers .= "Accept: ".$this->accept."\r\n"; + + if($this->use_gzip) { + // make sure PHP was built with --with-zlib + // and we can handle gzipp'ed data + if ( function_exists(gzinflate) ) { + $headers .= "Accept-encoding: gzip\r\n"; + } + else { + trigger_error( + "use_gzip is on, but PHP was built without zlib support.". + " Requesting file(s) without gzip encoding.", + E_USER_NOTICE); + } + } + + if(!empty($this->referer)) + $headers .= "Referer: ".$this->referer."\r\n"; + if(!empty($this->cookies)) + { + if(!is_array($this->cookies)) + $this->cookies = (array)$this->cookies; + + reset($this->cookies); + if ( count($this->cookies) > 0 ) { + $cookie_headers .= 'Cookie: '; + foreach ( $this->cookies as $cookieKey => $cookieVal ) { + $cookie_headers .= $cookieKey."=".urlencode($cookieVal)."; "; + } + $headers .= substr($cookie_headers,0,-2) . "\r\n"; + } + } + if(!empty($this->rawheaders)) + { + if(!is_array($this->rawheaders)) + $this->rawheaders = (array)$this->rawheaders; + while(list($headerKey,$headerVal) = each($this->rawheaders)) + $headers .= $headerKey.": ".$headerVal."\r\n"; + } + if(!empty($content_type)) { + $headers .= "Content-type: $content_type"; + if ($content_type == "multipart/form-data") + $headers .= "; boundary=".$this->_mime_boundary; + $headers .= "\r\n"; + } + if(!empty($body)) + $headers .= "Content-length: ".strlen($body)."\r\n"; + if(!empty($this->user) || !empty($this->pass)) + $headers .= "Authorization: BASIC ".base64_encode($this->user.":".$this->pass)."\r\n"; + + $headers .= "\r\n"; + + // set the read timeout if needed + if ($this->read_timeout > 0) + socket_set_timeout($fp, $this->read_timeout); + $this->timed_out = false; + + fwrite($fp,$headers.$body,strlen($headers.$body)); + + $this->_redirectaddr = false; + unset($this->headers); + + // content was returned gzip encoded? + $is_gzipped = false; + + while($currentHeader = fgets($fp,$this->_maxlinelen)) + { + if ($this->read_timeout > 0 && $this->_check_timeout($fp)) + { + $this->status=-100; + return false; + } + + // if($currentHeader == "\r\n") + if(preg_match("/^\r?\n$/", $currentHeader) ) + break; + + // if a header begins with Location: or URI:, set the redirect + if(preg_match("/^(Location:|URI:)/i",$currentHeader)) + { + // get URL portion of the redirect + preg_match("/^(Location:|URI:)\s+(.*)/",chop($currentHeader),$matches); + // look for :// in the Location header to see if hostname is included + if(!preg_match("|\:\/\/|",$matches[2])) + { + // no host in the path, so prepend + $this->_redirectaddr = $URI_PARTS["scheme"]."://".$this->host.":".$this->port; + // eliminate double slash + if(!preg_match("|^/|",$matches[2])) + $this->_redirectaddr .= "/".$matches[2]; + else + $this->_redirectaddr .= $matches[2]; + } + else + $this->_redirectaddr = $matches[2]; + } + + if(preg_match("|^HTTP/|",$currentHeader)) + { + if(preg_match("|^HTTP/[^\s]*\s(.*?)\s|",$currentHeader, $status)) + { + $this->status= $status[1]; + } + $this->response_code = $currentHeader; + } + + if (preg_match("/Content-Encoding: gzip/", $currentHeader) ) { + $is_gzipped = true; + } + + $this->headers[] = $currentHeader; + } + + # $results = fread($fp, $this->maxlength); + $results = ""; + while ( $data = fread($fp, $this->maxlength) ) { + $results .= $data; + if ( + strlen($results) > $this->maxlength ) { + break; + } + } + + // gunzip + if ( $is_gzipped ) { + // per http://www.php.net/manual/en/function.gzencode.php + $results = substr($results, 10); + $results = gzinflate($results); + } + + if ($this->read_timeout > 0 && $this->_check_timeout($fp)) + { + $this->status=-100; + return false; + } + + // check if there is a a redirect meta tag + + if(preg_match("']*?content[\s]*=[\s]*[\"\']?\d+;[\s]+URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i",$results,$match)) + { + $this->_redirectaddr = $this->_expandlinks($match[1],$URI); + } + + // have we hit our frame depth and is there frame src to fetch? + if(($this->_framedepth < $this->maxframes) && preg_match_all("']+)'i",$results,$match)) + { + $this->results[] = $results; + for($x=0; $x_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS["scheme"]."://".$this->host); + } + // have we already fetched framed content? + elseif(is_array($this->results)) + $this->results[] = $results; + // no framed content + else + $this->results = $results; + + return true; + } + +/*======================================================================*\ + Function: _httpsrequest + Purpose: go get the https data from the server using curl + Input: $url the url to fetch + $URI the full URI + $body body contents to send if any (POST) + Output: +\*======================================================================*/ + + function _httpsrequest($url,$URI,$http_method,$content_type="",$body="") + { + if($this->passcookies && $this->_redirectaddr) + $this->setcookies(); + + $headers = array(); + + $URI_PARTS = parse_url($URI); + if(empty($url)) + $url = "/"; + // GET ... header not needed for curl + //$headers[] = $http_method." ".$url." ".$this->_httpversion; + if(!empty($this->agent)) + $headers[] = "User-Agent: ".$this->agent; + if(!empty($this->host)) + $headers[] = "Host: ".$this->host; + if(!empty($this->accept)) + $headers[] = "Accept: ".$this->accept; + if(!empty($this->referer)) + $headers[] = "Referer: ".$this->referer; + if(!empty($this->cookies)) + { + if(!is_array($this->cookies)) + $this->cookies = (array)$this->cookies; + + reset($this->cookies); + if ( count($this->cookies) > 0 ) { + $cookie_str = 'Cookie: '; + foreach ( $this->cookies as $cookieKey => $cookieVal ) { + $cookie_str .= $cookieKey."=".urlencode($cookieVal)."; "; + } + $headers[] = substr($cookie_str,0,-2); + } + } + if(!empty($this->rawheaders)) + { + if(!is_array($this->rawheaders)) + $this->rawheaders = (array)$this->rawheaders; + while(list($headerKey,$headerVal) = each($this->rawheaders)) + $headers[] = $headerKey.": ".$headerVal; + } + if(!empty($content_type)) { + if ($content_type == "multipart/form-data") + $headers[] = "Content-type: $content_type; boundary=".$this->_mime_boundary; + else + $headers[] = "Content-type: $content_type"; + } + if(!empty($body)) + $headers[] = "Content-length: ".strlen($body); + if(!empty($this->user) || !empty($this->pass)) + $headers[] = "Authorization: BASIC ".base64_encode($this->user.":".$this->pass); + + for($curr_header = 0; $curr_header < count($headers); $curr_header++) { + $cmdline_params .= " -H \"".$headers[$curr_header]."\""; + } + + if(!empty($body)) + $cmdline_params .= " -d \"$body\""; + + if($this->read_timeout > 0) + $cmdline_params .= " -m ".$this->read_timeout; + + $headerfile = uniqid(time()); + + # accept self-signed certs + $cmdline_params .= " -k"; + exec($this->curl_path." -D \"/tmp/$headerfile\"".escapeshellcmd($cmdline_params)." ".escapeshellcmd($URI),$results,$return); + + if($return) + { + $this->error = "Error: cURL could not retrieve the document, error $return."; + return false; + } + + + $results = implode("\r\n",$results); + + $result_headers = file("/tmp/$headerfile"); + + $this->_redirectaddr = false; + unset($this->headers); + + for($currentHeader = 0; $currentHeader < count($result_headers); $currentHeader++) + { + + // if a header begins with Location: or URI:, set the redirect + if(preg_match("/^(Location: |URI: )/i",$result_headers[$currentHeader])) + { + // get URL portion of the redirect + preg_match("/^(Location: |URI:)(.*)/",chop($result_headers[$currentHeader]),$matches); + // look for :// in the Location header to see if hostname is included + if(!preg_match("|\:\/\/|",$matches[2])) + { + // no host in the path, so prepend + $this->_redirectaddr = $URI_PARTS["scheme"]."://".$this->host.":".$this->port; + // eliminate double slash + if(!preg_match("|^/|",$matches[2])) + $this->_redirectaddr .= "/".$matches[2]; + else + $this->_redirectaddr .= $matches[2]; + } + else + $this->_redirectaddr = $matches[2]; + } + + if(preg_match("|^HTTP/|",$result_headers[$currentHeader])) + { + $this->response_code = $result_headers[$currentHeader]; + if(preg_match("|^HTTP/[^\s]*\s(.*?)\s|",$this->response_code, $match)) + { + $this->status= $match[1]; + } + } + $this->headers[] = $result_headers[$currentHeader]; + } + + // check if there is a a redirect meta tag + + if(preg_match("']*?content[\s]*=[\s]*[\"\']?\d+;[\s]+URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i",$results,$match)) + { + $this->_redirectaddr = $this->_expandlinks($match[1],$URI); + } + + // have we hit our frame depth and is there frame src to fetch? + if(($this->_framedepth < $this->maxframes) && preg_match_all("']+)'i",$results,$match)) + { + $this->results[] = $results; + for($x=0; $x_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS["scheme"]."://".$this->host); + } + // have we already fetched framed content? + elseif(is_array($this->results)) + $this->results[] = $results; + // no framed content + else + $this->results = $results; + + unlink("/tmp/$headerfile"); + + return true; + } + +/*======================================================================*\ + Function: setcookies() + Purpose: set cookies for a redirection +\*======================================================================*/ + + function setcookies() + { + for($x=0; $xheaders); $x++) + { + if(preg_match("/^set-cookie:[\s]+([^=]+)=([^;]+)/i", $this->headers[$x],$match)) + $this->cookies[$match[1]] = $match[2]; + } + } + + +/*======================================================================*\ + Function: _check_timeout + Purpose: checks whether timeout has occurred + Input: $fp file pointer +\*======================================================================*/ + + function _check_timeout($fp) + { + if ($this->read_timeout > 0) { + $fp_status = socket_get_status($fp); + if ($fp_status["timed_out"]) { + $this->timed_out = true; + return true; + } + } + return false; + } + +/*======================================================================*\ + Function: _connect + Purpose: make a socket connection + Input: $fp file pointer +\*======================================================================*/ + + function _connect(&$fp) + { + if(!empty($this->proxy_host) && !empty($this->proxy_port)) + { + $this->_isproxy = true; + $host = $this->proxy_host; + $port = $this->proxy_port; + } + else + { + $host = $this->host; + $port = $this->port; + } + + $this->status = 0; + + if($fp = fsockopen( + $host, + $port, + $errno, + $errstr, + $this->_fp_timeout + )) + { + // socket connection succeeded + + return true; + } + else + { + // socket connection failed + $this->status = $errno; + switch($errno) + { + case -3: + $this->error="socket creation failed (-3)"; + case -4: + $this->error="dns lookup failure (-4)"; + case -5: + $this->error="connection refused or timed out (-5)"; + default: + $this->error="connection failed (".$errno.")"; + } + return false; + } + } +/*======================================================================*\ + Function: _disconnect + Purpose: disconnect a socket connection + Input: $fp file pointer +\*======================================================================*/ + + function _disconnect($fp) + { + return(fclose($fp)); + } + + +/*======================================================================*\ + Function: _prepare_post_body + Purpose: Prepare post body according to encoding type + Input: $formvars - form variables + $formfiles - form upload files + Output: post body +\*======================================================================*/ + + function _prepare_post_body($formvars, $formfiles) + { + settype($formvars, "array"); + settype($formfiles, "array"); + + if (count($formvars) == 0 && count($formfiles) == 0) + return; + + switch ($this->_submit_type) { + case "application/x-www-form-urlencoded": + reset($formvars); + while(list($key,$val) = each($formvars)) { + if (is_array($val) || is_object($val)) { + while (list($cur_key, $cur_val) = each($val)) { + $postdata .= urlencode($key)."[]=".urlencode($cur_val)."&"; + } + } else + $postdata .= urlencode($key)."=".urlencode($val)."&"; + } + break; + + case "multipart/form-data": + $this->_mime_boundary = "Snoopy".md5(uniqid(microtime())); + + reset($formvars); + while(list($key,$val) = each($formvars)) { + if (is_array($val) || is_object($val)) { + while (list($cur_key, $cur_val) = each($val)) { + $postdata .= "--".$this->_mime_boundary."\r\n"; + $postdata .= "Content-Disposition: form-data; name=\"$key\[\]\"\r\n\r\n"; + $postdata .= "$cur_val\r\n"; + } + } else { + $postdata .= "--".$this->_mime_boundary."\r\n"; + $postdata .= "Content-Disposition: form-data; name=\"$key\"\r\n\r\n"; + $postdata .= "$val\r\n"; + } + } + + reset($formfiles); + while (list($field_name, $file_names) = each($formfiles)) { + settype($file_names, "array"); + while (list(, $file_name) = each($file_names)) { + if (!is_readable($file_name)) continue; + + $fp = fopen($file_name, "r"); + $file_content = fread($fp, filesize($file_name)); + fclose($fp); + $base_name = basename($file_name); + + $postdata .= "--".$this->_mime_boundary."\r\n"; + $postdata .= "Content-Disposition: form-data; name=\"$field_name\"; filename=\"$base_name\"\r\n\r\n"; + $postdata .= "$file_content\r\n"; + } + } + $postdata .= "--".$this->_mime_boundary."--\r\n"; + break; + } + + return $postdata; + } +} + +?> + diff --git a/site/racecalendar/rating/styles/rating.css b/site/racecalendar/rating/styles/rating.css new file mode 100644 index 0000000..eec6d79 --- /dev/null +++ b/site/racecalendar/rating/styles/rating.css @@ -0,0 +1,102 @@ +/* star rating code - use lists because its more semantic */ +/* No javascript required */ +/* all the stars are contained in one matrix to solve rollover problems with delay */ +/* the background position is just shifted to reveal the correct image. */ +/* the images are 16px by 16px and the background position will be shifted in negative 16px increments */ +/* key: B=Blank : O=Orange : G = Green * / +/*..... The Matrix ....... */ +/* colours ....Background position */ +/* B B B B B - (0 0)*/ +/* G B B B B - (0 -16px)*/ +/* G G B B B - (0 -32px)*/ +/* G G G B B - (0 -48px)*/ +/* G G G G B - (0 -64px)*/ +/* G G G G G - (0 -80px)*/ +/* O B B B B - (0 -96px)*/ +/* O O B B B - (0 -112px)*/ +/* O O O B B - (0 -128px)*/ +/* O O O O B - (0 -144px)*/ +/* O O O O O - (0 -160px)*/ + + +/* the default rating is placed as a background image in the ul */ +/* use the background position according to the table above to display the required images*/ +.rating{ + width:80px; + height:16px; + margin:0px 0px 20px 0px; + padding:0; + list-style:none; + float: left; + position:relative; + background: url(../images/star-matrix.gif) no-repeat 0 0; +} +ul.rating li { + cursor: pointer; + /*ie5 mac doesn't like it if the list is floated\*/ + float:left; + /* end hide*/ + text-indent:-999em; +} +ul.rating li a { + position:absolute; + left:0; + top:0; + width:16px; + height:16px; + text-decoration:none; + z-index: 200; +} +ul.rating li.one a {left:0} +ul.rating li.two a {left:16px;} +ul.rating li.three a {left:32px;} +ul.rating li.four a {left:48px;} +ul.rating li.five a {left:64px;} +ul.rating li a:hover { + z-index:2; + width:80px; + height:16px; + overflow:hidden; + left:0; + background: url(../images/star-matrix.gif) no-repeat 0 0 +} +ul.rating li.one a:hover {background-position:0 -96px;} +ul.rating li.two a:hover {background-position:0 -112px;} +ul.rating li.three a:hover {background-position:0 -128px} +ul.rating li.four a:hover {background-position:0 -144px} +ul.rating li.five a:hover {background-position:0 -160px} + +/* this is used to remove the hover affect */ +/* use the background position according to the table above to display the required images*/ +.rated{ + width:80px; + height:16px; + margin:0px 0px 20px 0px; + padding:0; + list-style:none; + float: left; + position:relative; + background: url(../images/star-matrix.gif) no-repeat 0 0; +} +ul.rated li { + cursor: pointer; + /*ie5 mac doesn't like it if the list is floated\*/ + float:left; + /* end hide*/ + text-indent:-999em; +} +ul.rated li.one a {left:0} +ul.rated li.two a {left:16px;} +ul.rated li.three a {left:32px;} +ul.rated li.four a {left:48px;} +ul.rated li.five a {left:64px;} + +/* add these classes to the ul to effect the change to the correct number of stars */ +.nostar {background-position:0 0} +.onestar {background-position:0 -16px} +.twostar {background-position:0 -32px} +.threestar {background-position:0 -48px} +.fourstar {background-position:0 -64px} +.fivestar {background-position:0 -80px} +/* end rating code */ +h3{margin:0 0 2px 0;font-size:110%} \ No newline at end of file diff --git a/site/racecalendar/styles.css b/site/racecalendar/styles.css new file mode 100644 index 0000000..83c66fc --- /dev/null +++ b/site/racecalendar/styles.css @@ -0,0 +1,546 @@ +/* Defaults */ +body { +text-align: center; +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +color: #000; +padding: 0; +margin: 15px 0 5px; +} + +.indent { +margin-left: 10px; +} + +.indentlarge { +margin-left: 30px; +} + +.indenthuge { +margin-left: 50px; +} + +.rightside { +text-align: right; +} + +form { +margin: 0; +padding: 0; +} + +/* Links +a:link { +color: #000099; +text-decoration : none; +padding : 0; +margin : 0; +} +a:visited { +color: #000099; +text-decoration : none; +} +a:hover { +color: #000000; +text-decoration : underline; +} +a:active { +color: #999999; +text-decoration : none; +}*/ + +.modify{ +text-decoration: none; +color: #FFFFFF; +font-weight: bold; +font-size: 14pt; +} + +.modify:hover{ +text-decoration: underline; +} + +/* Outer layer */ + +#outer { +text-align: left; +background: #ffffff; +color: inherit; +/**border: 1px solid #000000;**/ +margin: 0 auto; +padding: 0; +} + +/* Header */ + +.header { +background: #fcce32; +} + +.logolink { +padding: 6px 3px; +} + + .logolink a { +background: inherit; +} + +.hdr-links { +width: 230px; +text-align: center; +font-size : 10px; +padding : 0; +margin: 0; +} + +a.hdrnav:link { +color: #000099; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +} +a.hdrnav:visited { +color: #000099; +background : inherit; +text-decoration : none; +} +a.hdrnav:hover { +color: #000; +background : #fff; +text-decoration : underline; +} +a.hdrnav:active { +color: #999; +background : inherit; +text-decoration : none; +} + +.hdr-links .access { +text-decoration : underline; +color : #000; +background : inherit; +} + + +/* Top Nav Bar */ + +.top-nav { +height: 25px; +background: #c5c6ac; +color: inherit; +border: solid #000000; +border-width: 1px 0; +} +.top-nav-inner { +padding: 5px 10px; +} +.top-nav-inner a:link { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: none; +} +.top-nav-inner a:visited { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: none; +} +.top-nav-inner a:hover { +background: inherit; +color: #555555; +font-weight: bold; +text-decoration: underline; +} +.top-nav-inner a:active { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: underline; +} + + + +/* Image Bar */ + +.image-bar { +clear:both; +} + +.title-img { +border: 1px solid #000; +color: #ffffff; +background: #343234; +padding: 0 0 0 10px; +} + +.section-title { +font-size: 16px; +font-weight: bold; +font-family: verdana, arial, tahoma, sans-serif; +} + +.graphic-img { +border: 1px solid #000000; +} + +.blank-img { +border: 1px solid #000000; +background: #fcce32; +} + +.blank-img-ads { +border: 1px solid #000000; +background: #fcce32; +} + + +/* Content Section */ + +.content { +padding: 0 4px 4px 4px; +margin: 0; +} + +.left-col { +border: 1px solid #000000; +background: #fcce32; +width: 145px; +} + +.left-nav { +margin: 3px; +} + +.statenav { + margin: 4px; +} + +.lnav-hdr { +background: #c5c6ac; +color: #000000; +font-family: verdana, tahoma, arial, sans-serif; +font-size: 11px; +font-weight: bold; +border: 1px solid #000000; +margin-top: 16px; +padding: 2px 5px; +} + +.left-nav p { +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +line-height: 14px; +margin:5px; +} + +.left-nav .access { +text-decoration : underline; +color : #000000; +background : inherit; +} + +a.lhdr:link { +color: #000000; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +font-weight: bold; +} +a.lhdr:visited { +color: #000000; +background : inherit; +text-decoration : none; +font-size: 10px; +font-weight: bold; +} +a.lhdr:hover { +color: #000; +background : #fcfefc; +text-decoration : underline; +} +a.lhdr:active { +color: #555; +background : inherit; +text-decoration : none; +} + +input.nobutton { + background-color: #0A50A1; + font-weight: bold; + font-size: 9px; + color: white; +} + +input.nobutton:visited { + background-color: #fcce32; + font-weight: bold; + font-size: 9px; + color: white; +} + +input.nobutton:active { + background-color: #fcce32 + color: white; + font-weight: bold; + font-size: 9px; +} + +a.nav:link { +color: #000099; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +} +a.nav:visited { +color: #000099; +background : inherit; +text-decoration : none; +} +a.nav:hover { +color: #000; +background : #fcfefc; +text-decoration : underline; +} +a.nav:active { +color: #999; +background : inherit; +text-decoration : none; +} + + + +/* Main content box */ +.content-box { +padding: 0; +margin: 0; +} + +/* right content box */ +.rhcol { +float: right; +width: 160px; +margin: 0 0 10px 15px; +background: #fcfefc; +color: #000000; +} +.rhcont { +border-left: 1px solid #000000; +border-bottom:3px solid #000000; +} +.rnav-hdr { +background: #c5c6ac; +color: #000000; +font-family: verdana, tahoma, arial, sans-serif; +font-size: 12px; +font-weight:bold; +border-bottom: 1px solid #000; +border-left: 2px solid #000000; +margin: 3px 0; +padding: 2px; +} + +/* main content area */ + +.content-title { +margin: 5px 0px 15px; +font-size: 14px; +text-decoration: underline; +} + +.bottomindent { +margin-bottom: 5px; +} + +.content-title-blog { +margin: 5px 0px 2px; +margin-left: 8px; +font-size: 16px; +text-decoration: none; +} + +.content-title-no1 { +margin: 5px 0px 15px; +font-size: 14px; +text-decoration: none; +} + +.content-title-mod { +margin: 5px 10px 15px; +font-size: 14px; +text-decoration: underline; +} + +.content-title-no { +margin: 0px 0px 0px; +font-size: 12px; +text-decoration: none; +} + +.content-box .cont { +margin: 10px 10px 15px 10px; +font-size: 11px; +line-height: 15px; +} + +.content-box .cont-list { +margin: 2px 10px 5px 10px; +} + +.content-box .sub-hdr { +margin: 2px 10px 10px; +font-size: 12px; +text-decoration: underline; +} + +.content-box .sub-hdr-mod { +margin: 2px 10px 10px; +font-size: 11px; +text-decoration: none; +} + +/* Bottom footer box */ + +.footer-col { +margin: 0 0 0 4px; +} + +.footer-box { +border: 1px solid #000000; +background:#343234; +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +color:#ffffff; +padding: 0 0 0 15px; +} + +.footer-box .cont1 { +margin: 8px 10px 5px 5px; +font-size: 11px; +line-height: 15px; +} +.footer-box .cont2 { +margin: 2px 10px 15px 5px; +font-size: 11px; +line-height: 15px; +} + +.footer-box input { +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +} + +/* Copyright layer */ + +.copyright { +background: #c5c6ac; +font-family: verdana, arial, tahoma, sans-serif; +font-size: 10px; +font-weight: bold; +padding: 6px; +border: solid #000000; +border-width: 1px 0 0; +} + +.rh-ad { +padding: 10px 0; +} + +/* Terms of use on front page */ +.tou { +margin: 0 0 0 10px; +} + + +/* for templates info */ +.temp-lh { +float : left; +width : 100px; +margin : 0; +padding : 2px 10px 2px 0; +text-align : left; +} +.temp-rh { +margin : 0; +padding : 2px 0; +text-align : left; +} +.date { +font-size : 10px; +color : #787d63; +background : inherit; +} +.temp-list ul { +font-size : 11px; +margin : 0; +padding : 0; +list-style-type : none; +} +.temp-list { +padding : 0 0 0 15px; +} + +/* small*/ +small.fsize { +font-size : 9px; +color : #999999; +background : inherit; +} + +.code { +color : #663300; +background : #eeeeee; +font-family : verdana, arial, sans-serif; +font-size : 11px; +line-height : 20px; +padding : 4px 10px; +margin : 10px 20px; +border : 1px dashed #999999; +} + + +/* Search form on front page */ + +.search-form { +color: #000; +background: #c5c6ac; +padding: 5px; +margin: 0px 0 10px 30px; +border: 1px solid #333333; +width:465px; +text-align: center; +white-space: nowrap; +} + +/* Search forms on Search page */ + +.adv-search { +margin: 0 0 0 15px; +} + +/* Tool Tip */ +.tip { +cursor : help; +border-bottom : 1px dashed #555555; +} + +/* ADVERTISEMENTS */ +/* container div is 'id=adspace' */ +#adspace { +background: inherit; +} +.ads { +background: inherit; +margin: 5px 0 15px 4px; +font-size: 11px; +padding: 5px; +border: solid #555; +border-width: 1px 0 0 1px; +} +.ads a:link { font-size: 11px; } diff --git a/site/retailers/extlib/Snoopy.class.inc b/site/retailers/extlib/Snoopy.class.inc new file mode 100644 index 0000000..22dc434 --- /dev/null +++ b/site/retailers/extlib/Snoopy.class.inc @@ -0,0 +1,901 @@ + +Copyright (c): 1999-2000 ispi, all rights reserved +Version: 1.0 + + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +You may contact the author of Snoopy by e-mail at: +monte@ispi.net + +Or, write to: +Monte Ohrt +CTO, ispi +237 S. 70th suite 220 +Lincoln, NE 68510 + +The latest version of Snoopy can be obtained from: +http://snoopy.sourceforge.com + +*************************************************/ + +class Snoopy +{ + /**** Public variables ****/ + + /* user definable vars */ + + var $host = "www.php.net"; // host name we are connecting to + var $port = 80; // port we are connecting to + var $proxy_host = ""; // proxy host to use + var $proxy_port = ""; // proxy port to use + var $agent = "Snoopy v1.0"; // agent we masquerade as + var $referer = ""; // referer info to pass + var $cookies = array(); // array of cookies to pass + // $cookies["username"]="joe"; + var $rawheaders = array(); // array of raw headers to send + // $rawheaders["Content-type"]="text/html"; + + var $maxredirs = 5; // http redirection depth maximum. 0 = disallow + var $lastredirectaddr = ""; // contains address of last redirected address + var $offsiteok = true; // allows redirection off-site + var $maxframes = 0; // frame content depth maximum. 0 = disallow + var $expandlinks = true; // expand links to fully qualified URLs. + // this only applies to fetchlinks() + // or submitlinks() + var $passcookies = true; // pass set cookies back through redirects + // NOTE: this currently does not respect + // dates, domains or paths. + + var $user = ""; // user for http authentication + var $pass = ""; // password for http authentication + + // http accept types + var $accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*"; + + var $results = ""; // where the content is put + + var $error = ""; // error messages sent here + var $response_code = ""; // response code returned from server + var $headers = array(); // headers returned from server sent here + var $maxlength = 500000; // max return data length (body) + var $read_timeout = 0; // timeout on read operations, in seconds + // supported only since PHP 4 Beta 4 + // set to 0 to disallow timeouts + var $timed_out = false; // if a read operation timed out + var $status = 0; // http request status + + var $curl_path = "/usr/bin/curl"; + // Snoopy will use cURL for fetching + // SSL content if a full system path to + // the cURL binary is supplied here. + // set to false if you do not have + // cURL installed. See http://curl.haxx.se + // for details on installing cURL. + // Snoopy does *not* use the cURL + // library functions built into php, + // as these functions are not stable + // as of this Snoopy release. + + // send Accept-encoding: gzip? + var $use_gzip = true; + + /**** Private variables ****/ + + var $_maxlinelen = 4096; // max line length (headers) + + var $_httpmethod = "GET"; // default http request method + var $_httpversion = "HTTP/1.0"; // default http request version + var $_submit_method = "POST"; // default submit method + var $_submit_type = "application/x-www-form-urlencoded"; // default submit type + var $_mime_boundary = ""; // MIME boundary for multipart/form-data submit type + var $_redirectaddr = false; // will be set if page fetched is a redirect + var $_redirectdepth = 0; // increments on an http redirect + var $_frameurls = array(); // frame src urls + var $_framedepth = 0; // increments on frame depth + + var $_isproxy = false; // set if using a proxy server + var $_fp_timeout = 30; // timeout for socket connection + +/*======================================================================*\ + Function: fetch + Purpose: fetch the contents of a web page + (and possibly other protocols in the + future like ftp, nntp, gopher, etc.) + Input: $URI the location of the page to fetch + Output: $this->results the output text from the fetch +\*======================================================================*/ + + function fetch($URI) + { + + //preg_match("|^([^:]+)://([^:/]+)(:[\d]+)*(.*)|",$URI,$URI_PARTS); + $URI_PARTS = parse_url($URI); + if (!empty($URI_PARTS["user"])) + $this->user = $URI_PARTS["user"]; + if (!empty($URI_PARTS["pass"])) + $this->pass = $URI_PARTS["pass"]; + + switch($URI_PARTS["scheme"]) + { + case "http": + $this->host = $URI_PARTS["host"]; + if(!empty($URI_PARTS["port"])) + $this->port = $URI_PARTS["port"]; + if($this->_connect($fp)) + { + if($this->_isproxy) + { + // using proxy, send entire URI + $this->_httprequest($URI,$fp,$URI,$this->_httpmethod); + } + else + { + $path = $URI_PARTS["path"].(isset($URI_PARTS["query"]) ? "?".$URI_PARTS["query"] : ""); + // no proxy, send only the path + $this->_httprequest($path, $fp, $URI, $this->_httpmethod); + } + + $this->_disconnect($fp); + + if($this->_redirectaddr) + { + /* url was redirected, check if we've hit the max depth */ + if($this->maxredirs > $this->_redirectdepth) + { + // only follow redirect if it's on this site, or offsiteok is true + if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok) + { + /* follow the redirect */ + $this->_redirectdepth++; + $this->lastredirectaddr=$this->_redirectaddr; + $this->fetch($this->_redirectaddr); + } + } + } + + if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0) + { + $frameurls = $this->_frameurls; + $this->_frameurls = array(); + + while(list(,$frameurl) = each($frameurls)) + { + if($this->_framedepth < $this->maxframes) + { + $this->fetch($frameurl); + $this->_framedepth++; + } + else + break; + } + } + } + else + { + return false; + } + return true; + break; + case "https": + if(!$this->curl_path || (!is_executable($this->curl_path))) { + $this->error = "Bad curl ($this->curl_path), can't fetch HTTPS \n"; + return false; + } + $this->host = $URI_PARTS["host"]; + if(!empty($URI_PARTS["port"])) + $this->port = $URI_PARTS["port"]; + if($this->_isproxy) + { + // using proxy, send entire URI + $this->_httpsrequest($URI,$URI,$this->_httpmethod); + } + else + { + $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : ""); + // no proxy, send only the path + $this->_httpsrequest($path, $URI, $this->_httpmethod); + } + + if($this->_redirectaddr) + { + /* url was redirected, check if we've hit the max depth */ + if($this->maxredirs > $this->_redirectdepth) + { + // only follow redirect if it's on this site, or offsiteok is true + if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok) + { + /* follow the redirect */ + $this->_redirectdepth++; + $this->lastredirectaddr=$this->_redirectaddr; + $this->fetch($this->_redirectaddr); + } + } + } + + if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0) + { + $frameurls = $this->_frameurls; + $this->_frameurls = array(); + + while(list(,$frameurl) = each($frameurls)) + { + if($this->_framedepth < $this->maxframes) + { + $this->fetch($frameurl); + $this->_framedepth++; + } + else + break; + } + } + return true; + break; + default: + // not a valid protocol + $this->error = 'Invalid protocol "'.$URI_PARTS["scheme"].'"\n'; + return false; + break; + } + return true; + } + + + +/*======================================================================*\ + Private functions +\*======================================================================*/ + + +/*======================================================================*\ + Function: _striplinks + Purpose: strip the hyperlinks from an html document + Input: $document document to strip. + Output: $match an array of the links +\*======================================================================*/ + + function _striplinks($document) + { + preg_match_all("'<\s*a\s+.*href\s*=\s* # find ]+)) # if quote found, match up to next matching + # quote, otherwise match up to next space + 'isx",$document,$links); + + + // catenate the non-empty matches from the conditional subpattern + + while(list($key,$val) = each($links[2])) + { + if(!empty($val)) + $match[] = $val; + } + + while(list($key,$val) = each($links[3])) + { + if(!empty($val)) + $match[] = $val; + } + + // return the links + return $match; + } + +/*======================================================================*\ + Function: _stripform + Purpose: strip the form elements from an html document + Input: $document document to strip. + Output: $match an array of the links +\*======================================================================*/ + + function _stripform($document) + { + preg_match_all("'<\/?(FORM|INPUT|SELECT|TEXTAREA|(OPTION))[^<>]*>(?(2)(.*(?=<\/?(option|select)[^<>]*>[\r\n]*)|(?=[\r\n]*))|(?=[\r\n]*))'Usi",$document,$elements); + + // catenate the matches + $match = implode("\r\n",$elements[0]); + + // return the links + return $match; + } + + + +/*======================================================================*\ + Function: _striptext + Purpose: strip the text from an html document + Input: $document document to strip. + Output: $text the resulting text +\*======================================================================*/ + + function _striptext($document) + { + + // I didn't use preg eval (//e) since that is only available in PHP 4.0. + // so, list your entities one by one here. I included some of the + // more common ones. + + $search = array("']*?>.*?'si", // strip out javascript + "'<[\/\!]*?[^<>]*?>'si", // strip out html tags + "'([\r\n])[\s]+'", // strip out white space + "'&(quote|#34);'i", // replace html entities + "'&(amp|#38);'i", + "'&(lt|#60);'i", + "'&(gt|#62);'i", + "'&(nbsp|#160);'i", + "'&(iexcl|#161);'i", + "'&(cent|#162);'i", + "'&(pound|#163);'i", + "'&(copy|#169);'i" + ); + $replace = array( "", + "", + "\\1", + "\"", + "&", + "<", + ">", + " ", + chr(161), + chr(162), + chr(163), + chr(169)); + + $text = preg_replace($search,$replace,$document); + + return $text; + } + +/*======================================================================*\ + Function: _expandlinks + Purpose: expand each link into a fully qualified URL + Input: $links the links to qualify + $URI the full URI to get the base from + Output: $expandedLinks the expanded links +\*======================================================================*/ + + function _expandlinks($links,$URI) + { + + preg_match("/^[^\?]+/",$URI,$match); + + $match = preg_replace("|/[^\/\.]+\.[^\/\.]+$|","",$match[0]); + + $search = array( "|^http://".preg_quote($this->host)."|i", + "|^(?!http://)(\/)?(?!mailto:)|i", + "|/\./|", + "|/[^\/]+/\.\./|" + ); + + $replace = array( "", + $match."/", + "/", + "/" + ); + + $expandedLinks = preg_replace($search,$replace,$links); + + return $expandedLinks; + } + +/*======================================================================*\ + Function: _httprequest + Purpose: go get the http data from the server + Input: $url the url to fetch + $fp the current open file pointer + $URI the full URI + $body body contents to send if any (POST) + Output: +\*======================================================================*/ + + function _httprequest($url,$fp,$URI,$http_method,$content_type="",$body="") + { + if($this->passcookies && $this->_redirectaddr) + $this->setcookies(); + + $URI_PARTS = parse_url($URI); + if(empty($url)) + $url = "/"; + $headers = $http_method." ".$url." ".$this->_httpversion."\r\n"; + if(!empty($this->agent)) + $headers .= "User-Agent: ".$this->agent."\r\n"; + if(!empty($this->host) && !isset($this->rawheaders['Host'])) + $headers .= "Host: ".$this->host."\r\n"; + if(!empty($this->accept)) + $headers .= "Accept: ".$this->accept."\r\n"; + + if($this->use_gzip) { + // make sure PHP was built with --with-zlib + // and we can handle gzipp'ed data + if ( function_exists(gzinflate) ) { + $headers .= "Accept-encoding: gzip\r\n"; + } + else { + trigger_error( + "use_gzip is on, but PHP was built without zlib support.". + " Requesting file(s) without gzip encoding.", + E_USER_NOTICE); + } + } + + if(!empty($this->referer)) + $headers .= "Referer: ".$this->referer."\r\n"; + if(!empty($this->cookies)) + { + if(!is_array($this->cookies)) + $this->cookies = (array)$this->cookies; + + reset($this->cookies); + if ( count($this->cookies) > 0 ) { + $cookie_headers .= 'Cookie: '; + foreach ( $this->cookies as $cookieKey => $cookieVal ) { + $cookie_headers .= $cookieKey."=".urlencode($cookieVal)."; "; + } + $headers .= substr($cookie_headers,0,-2) . "\r\n"; + } + } + if(!empty($this->rawheaders)) + { + if(!is_array($this->rawheaders)) + $this->rawheaders = (array)$this->rawheaders; + while(list($headerKey,$headerVal) = each($this->rawheaders)) + $headers .= $headerKey.": ".$headerVal."\r\n"; + } + if(!empty($content_type)) { + $headers .= "Content-type: $content_type"; + if ($content_type == "multipart/form-data") + $headers .= "; boundary=".$this->_mime_boundary; + $headers .= "\r\n"; + } + if(!empty($body)) + $headers .= "Content-length: ".strlen($body)."\r\n"; + if(!empty($this->user) || !empty($this->pass)) + $headers .= "Authorization: BASIC ".base64_encode($this->user.":".$this->pass)."\r\n"; + + $headers .= "\r\n"; + + // set the read timeout if needed + if ($this->read_timeout > 0) + socket_set_timeout($fp, $this->read_timeout); + $this->timed_out = false; + + fwrite($fp,$headers.$body,strlen($headers.$body)); + + $this->_redirectaddr = false; + unset($this->headers); + + // content was returned gzip encoded? + $is_gzipped = false; + + while($currentHeader = fgets($fp,$this->_maxlinelen)) + { + if ($this->read_timeout > 0 && $this->_check_timeout($fp)) + { + $this->status=-100; + return false; + } + + // if($currentHeader == "\r\n") + if(preg_match("/^\r?\n$/", $currentHeader) ) + break; + + // if a header begins with Location: or URI:, set the redirect + if(preg_match("/^(Location:|URI:)/i",$currentHeader)) + { + // get URL portion of the redirect + preg_match("/^(Location:|URI:)\s+(.*)/",chop($currentHeader),$matches); + // look for :// in the Location header to see if hostname is included + if(!preg_match("|\:\/\/|",$matches[2])) + { + // no host in the path, so prepend + $this->_redirectaddr = $URI_PARTS["scheme"]."://".$this->host.":".$this->port; + // eliminate double slash + if(!preg_match("|^/|",$matches[2])) + $this->_redirectaddr .= "/".$matches[2]; + else + $this->_redirectaddr .= $matches[2]; + } + else + $this->_redirectaddr = $matches[2]; + } + + if(preg_match("|^HTTP/|",$currentHeader)) + { + if(preg_match("|^HTTP/[^\s]*\s(.*?)\s|",$currentHeader, $status)) + { + $this->status= $status[1]; + } + $this->response_code = $currentHeader; + } + + if (preg_match("/Content-Encoding: gzip/", $currentHeader) ) { + $is_gzipped = true; + } + + $this->headers[] = $currentHeader; + } + + # $results = fread($fp, $this->maxlength); + $results = ""; + while ( $data = fread($fp, $this->maxlength) ) { + $results .= $data; + if ( + strlen($results) > $this->maxlength ) { + break; + } + } + + // gunzip + if ( $is_gzipped ) { + // per http://www.php.net/manual/en/function.gzencode.php + $results = substr($results, 10); + $results = gzinflate($results); + } + + if ($this->read_timeout > 0 && $this->_check_timeout($fp)) + { + $this->status=-100; + return false; + } + + // check if there is a a redirect meta tag + + if(preg_match("']*?content[\s]*=[\s]*[\"\']?\d+;[\s]+URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i",$results,$match)) + { + $this->_redirectaddr = $this->_expandlinks($match[1],$URI); + } + + // have we hit our frame depth and is there frame src to fetch? + if(($this->_framedepth < $this->maxframes) && preg_match_all("']+)'i",$results,$match)) + { + $this->results[] = $results; + for($x=0; $x_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS["scheme"]."://".$this->host); + } + // have we already fetched framed content? + elseif(is_array($this->results)) + $this->results[] = $results; + // no framed content + else + $this->results = $results; + + return true; + } + +/*======================================================================*\ + Function: _httpsrequest + Purpose: go get the https data from the server using curl + Input: $url the url to fetch + $URI the full URI + $body body contents to send if any (POST) + Output: +\*======================================================================*/ + + function _httpsrequest($url,$URI,$http_method,$content_type="",$body="") + { + if($this->passcookies && $this->_redirectaddr) + $this->setcookies(); + + $headers = array(); + + $URI_PARTS = parse_url($URI); + if(empty($url)) + $url = "/"; + // GET ... header not needed for curl + //$headers[] = $http_method." ".$url." ".$this->_httpversion; + if(!empty($this->agent)) + $headers[] = "User-Agent: ".$this->agent; + if(!empty($this->host)) + $headers[] = "Host: ".$this->host; + if(!empty($this->accept)) + $headers[] = "Accept: ".$this->accept; + if(!empty($this->referer)) + $headers[] = "Referer: ".$this->referer; + if(!empty($this->cookies)) + { + if(!is_array($this->cookies)) + $this->cookies = (array)$this->cookies; + + reset($this->cookies); + if ( count($this->cookies) > 0 ) { + $cookie_str = 'Cookie: '; + foreach ( $this->cookies as $cookieKey => $cookieVal ) { + $cookie_str .= $cookieKey."=".urlencode($cookieVal)."; "; + } + $headers[] = substr($cookie_str,0,-2); + } + } + if(!empty($this->rawheaders)) + { + if(!is_array($this->rawheaders)) + $this->rawheaders = (array)$this->rawheaders; + while(list($headerKey,$headerVal) = each($this->rawheaders)) + $headers[] = $headerKey.": ".$headerVal; + } + if(!empty($content_type)) { + if ($content_type == "multipart/form-data") + $headers[] = "Content-type: $content_type; boundary=".$this->_mime_boundary; + else + $headers[] = "Content-type: $content_type"; + } + if(!empty($body)) + $headers[] = "Content-length: ".strlen($body); + if(!empty($this->user) || !empty($this->pass)) + $headers[] = "Authorization: BASIC ".base64_encode($this->user.":".$this->pass); + + for($curr_header = 0; $curr_header < count($headers); $curr_header++) { + $cmdline_params .= " -H \"".$headers[$curr_header]."\""; + } + + if(!empty($body)) + $cmdline_params .= " -d \"$body\""; + + if($this->read_timeout > 0) + $cmdline_params .= " -m ".$this->read_timeout; + + $headerfile = uniqid(time()); + + # accept self-signed certs + $cmdline_params .= " -k"; + exec($this->curl_path." -D \"/tmp/$headerfile\"".escapeshellcmd($cmdline_params)." ".escapeshellcmd($URI),$results,$return); + + if($return) + { + $this->error = "Error: cURL could not retrieve the document, error $return."; + return false; + } + + + $results = implode("\r\n",$results); + + $result_headers = file("/tmp/$headerfile"); + + $this->_redirectaddr = false; + unset($this->headers); + + for($currentHeader = 0; $currentHeader < count($result_headers); $currentHeader++) + { + + // if a header begins with Location: or URI:, set the redirect + if(preg_match("/^(Location: |URI: )/i",$result_headers[$currentHeader])) + { + // get URL portion of the redirect + preg_match("/^(Location: |URI:)(.*)/",chop($result_headers[$currentHeader]),$matches); + // look for :// in the Location header to see if hostname is included + if(!preg_match("|\:\/\/|",$matches[2])) + { + // no host in the path, so prepend + $this->_redirectaddr = $URI_PARTS["scheme"]."://".$this->host.":".$this->port; + // eliminate double slash + if(!preg_match("|^/|",$matches[2])) + $this->_redirectaddr .= "/".$matches[2]; + else + $this->_redirectaddr .= $matches[2]; + } + else + $this->_redirectaddr = $matches[2]; + } + + if(preg_match("|^HTTP/|",$result_headers[$currentHeader])) + { + $this->response_code = $result_headers[$currentHeader]; + if(preg_match("|^HTTP/[^\s]*\s(.*?)\s|",$this->response_code, $match)) + { + $this->status= $match[1]; + } + } + $this->headers[] = $result_headers[$currentHeader]; + } + + // check if there is a a redirect meta tag + + if(preg_match("']*?content[\s]*=[\s]*[\"\']?\d+;[\s]+URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i",$results,$match)) + { + $this->_redirectaddr = $this->_expandlinks($match[1],$URI); + } + + // have we hit our frame depth and is there frame src to fetch? + if(($this->_framedepth < $this->maxframes) && preg_match_all("']+)'i",$results,$match)) + { + $this->results[] = $results; + for($x=0; $x_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS["scheme"]."://".$this->host); + } + // have we already fetched framed content? + elseif(is_array($this->results)) + $this->results[] = $results; + // no framed content + else + $this->results = $results; + + unlink("/tmp/$headerfile"); + + return true; + } + +/*======================================================================*\ + Function: setcookies() + Purpose: set cookies for a redirection +\*======================================================================*/ + + function setcookies() + { + for($x=0; $xheaders); $x++) + { + if(preg_match("/^set-cookie:[\s]+([^=]+)=([^;]+)/i", $this->headers[$x],$match)) + $this->cookies[$match[1]] = $match[2]; + } + } + + +/*======================================================================*\ + Function: _check_timeout + Purpose: checks whether timeout has occurred + Input: $fp file pointer +\*======================================================================*/ + + function _check_timeout($fp) + { + if ($this->read_timeout > 0) { + $fp_status = socket_get_status($fp); + if ($fp_status["timed_out"]) { + $this->timed_out = true; + return true; + } + } + return false; + } + +/*======================================================================*\ + Function: _connect + Purpose: make a socket connection + Input: $fp file pointer +\*======================================================================*/ + + function _connect(&$fp) + { + if(!empty($this->proxy_host) && !empty($this->proxy_port)) + { + $this->_isproxy = true; + $host = $this->proxy_host; + $port = $this->proxy_port; + } + else + { + $host = $this->host; + $port = $this->port; + } + + $this->status = 0; + + if($fp = fsockopen( + $host, + $port, + $errno, + $errstr, + $this->_fp_timeout + )) + { + // socket connection succeeded + + return true; + } + else + { + // socket connection failed + $this->status = $errno; + switch($errno) + { + case -3: + $this->error="socket creation failed (-3)"; + case -4: + $this->error="dns lookup failure (-4)"; + case -5: + $this->error="connection refused or timed out (-5)"; + default: + $this->error="connection failed (".$errno.")"; + } + return false; + } + } +/*======================================================================*\ + Function: _disconnect + Purpose: disconnect a socket connection + Input: $fp file pointer +\*======================================================================*/ + + function _disconnect($fp) + { + return(fclose($fp)); + } + + +/*======================================================================*\ + Function: _prepare_post_body + Purpose: Prepare post body according to encoding type + Input: $formvars - form variables + $formfiles - form upload files + Output: post body +\*======================================================================*/ + + function _prepare_post_body($formvars, $formfiles) + { + settype($formvars, "array"); + settype($formfiles, "array"); + + if (count($formvars) == 0 && count($formfiles) == 0) + return; + + switch ($this->_submit_type) { + case "application/x-www-form-urlencoded": + reset($formvars); + while(list($key,$val) = each($formvars)) { + if (is_array($val) || is_object($val)) { + while (list($cur_key, $cur_val) = each($val)) { + $postdata .= urlencode($key)."[]=".urlencode($cur_val)."&"; + } + } else + $postdata .= urlencode($key)."=".urlencode($val)."&"; + } + break; + + case "multipart/form-data": + $this->_mime_boundary = "Snoopy".md5(uniqid(microtime())); + + reset($formvars); + while(list($key,$val) = each($formvars)) { + if (is_array($val) || is_object($val)) { + while (list($cur_key, $cur_val) = each($val)) { + $postdata .= "--".$this->_mime_boundary."\r\n"; + $postdata .= "Content-Disposition: form-data; name=\"$key\[\]\"\r\n\r\n"; + $postdata .= "$cur_val\r\n"; + } + } else { + $postdata .= "--".$this->_mime_boundary."\r\n"; + $postdata .= "Content-Disposition: form-data; name=\"$key\"\r\n\r\n"; + $postdata .= "$val\r\n"; + } + } + + reset($formfiles); + while (list($field_name, $file_names) = each($formfiles)) { + settype($file_names, "array"); + while (list(, $file_name) = each($file_names)) { + if (!is_readable($file_name)) continue; + + $fp = fopen($file_name, "r"); + $file_content = fread($fp, filesize($file_name)); + fclose($fp); + $base_name = basename($file_name); + + $postdata .= "--".$this->_mime_boundary."\r\n"; + $postdata .= "Content-Disposition: form-data; name=\"$field_name\"; filename=\"$base_name\"\r\n\r\n"; + $postdata .= "$file_content\r\n"; + } + } + $postdata .= "--".$this->_mime_boundary."--\r\n"; + break; + } + + return $postdata; + } +} + +?> + diff --git a/site/retailers/rating/styles/rating.css b/site/retailers/rating/styles/rating.css new file mode 100644 index 0000000..eec6d79 --- /dev/null +++ b/site/retailers/rating/styles/rating.css @@ -0,0 +1,102 @@ +/* star rating code - use lists because its more semantic */ +/* No javascript required */ +/* all the stars are contained in one matrix to solve rollover problems with delay */ +/* the background position is just shifted to reveal the correct image. */ +/* the images are 16px by 16px and the background position will be shifted in negative 16px increments */ +/* key: B=Blank : O=Orange : G = Green * / +/*..... The Matrix ....... */ +/* colours ....Background position */ +/* B B B B B - (0 0)*/ +/* G B B B B - (0 -16px)*/ +/* G G B B B - (0 -32px)*/ +/* G G G B B - (0 -48px)*/ +/* G G G G B - (0 -64px)*/ +/* G G G G G - (0 -80px)*/ +/* O B B B B - (0 -96px)*/ +/* O O B B B - (0 -112px)*/ +/* O O O B B - (0 -128px)*/ +/* O O O O B - (0 -144px)*/ +/* O O O O O - (0 -160px)*/ + + +/* the default rating is placed as a background image in the ul */ +/* use the background position according to the table above to display the required images*/ +.rating{ + width:80px; + height:16px; + margin:0px 0px 20px 0px; + padding:0; + list-style:none; + float: left; + position:relative; + background: url(../images/star-matrix.gif) no-repeat 0 0; +} +ul.rating li { + cursor: pointer; + /*ie5 mac doesn't like it if the list is floated\*/ + float:left; + /* end hide*/ + text-indent:-999em; +} +ul.rating li a { + position:absolute; + left:0; + top:0; + width:16px; + height:16px; + text-decoration:none; + z-index: 200; +} +ul.rating li.one a {left:0} +ul.rating li.two a {left:16px;} +ul.rating li.three a {left:32px;} +ul.rating li.four a {left:48px;} +ul.rating li.five a {left:64px;} +ul.rating li a:hover { + z-index:2; + width:80px; + height:16px; + overflow:hidden; + left:0; + background: url(../images/star-matrix.gif) no-repeat 0 0 +} +ul.rating li.one a:hover {background-position:0 -96px;} +ul.rating li.two a:hover {background-position:0 -112px;} +ul.rating li.three a:hover {background-position:0 -128px} +ul.rating li.four a:hover {background-position:0 -144px} +ul.rating li.five a:hover {background-position:0 -160px} + +/* this is used to remove the hover affect */ +/* use the background position according to the table above to display the required images*/ +.rated{ + width:80px; + height:16px; + margin:0px 0px 20px 0px; + padding:0; + list-style:none; + float: left; + position:relative; + background: url(../images/star-matrix.gif) no-repeat 0 0; +} +ul.rated li { + cursor: pointer; + /*ie5 mac doesn't like it if the list is floated\*/ + float:left; + /* end hide*/ + text-indent:-999em; +} +ul.rated li.one a {left:0} +ul.rated li.two a {left:16px;} +ul.rated li.three a {left:32px;} +ul.rated li.four a {left:48px;} +ul.rated li.five a {left:64px;} + +/* add these classes to the ul to effect the change to the correct number of stars */ +.nostar {background-position:0 0} +.onestar {background-position:0 -16px} +.twostar {background-position:0 -32px} +.threestar {background-position:0 -48px} +.fourstar {background-position:0 -64px} +.fivestar {background-position:0 -80px} +/* end rating code */ +h3{margin:0 0 2px 0;font-size:110%} \ No newline at end of file diff --git a/site/retailers/styles.css b/site/retailers/styles.css new file mode 100644 index 0000000..d2bd5d1 --- /dev/null +++ b/site/retailers/styles.css @@ -0,0 +1,545 @@ +/* Defaults */ +body { +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +color: #000; +padding: 0; +margin: 15px 0 5px; +} + +.indent { +margin-left: 10px; +} + +.indentlarge { +margin-left: 30px; +} + +.indenthuge { +margin-left: 50px; +} + +.rightside { +text-align: right; +} + +form { +margin: 0; +padding: 0; +} + +/* Links +a:link { +color: #000099; +text-decoration : none; +padding : 0; +margin : 0; +} +a:visited { +color: #000099; +text-decoration : none; +} +a:hover { +color: #000000; +text-decoration : underline; +} +a:active { +color: #999999; +text-decoration : none; +}*/ + +.modify{ +text-decoration: none; +color: #FFFFFF; +font-weight: bold; +font-size: 14pt; +} + +.modify:hover{ +text-decoration: underline; +} + +/* Outer layer */ + +#outer { +text-align: left; +background: #ffffff; +color: inherit; +/**border: 1px solid #000000;**/ +margin: 0 auto; +padding: 0; +} + +/* Header */ + +.header { +background: #fcce32; +} + +.logolink { +padding: 6px 3px; +} + + .logolink a { +background: inherit; +} + +.hdr-links { +width: 230px; +text-align: center; +font-size : 10px; +padding : 0; +margin: 0; +} + +a.hdrnav:link { +color: #000099; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +} +a.hdrnav:visited { +color: #000099; +background : inherit; +text-decoration : none; +} +a.hdrnav:hover { +color: #000; +background : #fff; +text-decoration : underline; +} +a.hdrnav:active { +color: #999; +background : inherit; +text-decoration : none; +} + +.hdr-links .access { +text-decoration : underline; +color : #000; +background : inherit; +} + + +/* Top Nav Bar */ + +.top-nav { +height: 25px; +background: #c5c6ac; +color: inherit; +border: solid #000000; +border-width: 1px 0; +} +.top-nav-inner { +padding: 5px 10px; +} +.top-nav-inner a:link { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: none; +} +.top-nav-inner a:visited { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: none; +} +.top-nav-inner a:hover { +background: inherit; +color: #555555; +font-weight: bold; +text-decoration: underline; +} +.top-nav-inner a:active { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: underline; +} + + + +/* Image Bar */ + +.image-bar { +clear:both; +} + +.title-img { +border: 1px solid #000; +color: #ffffff; +background: #343234; +padding: 0 0 0 10px; +} + +.section-title { +font-size: 16px; +font-weight: bold; +font-family: verdana, arial, tahoma, sans-serif; +} + +.graphic-img { +border: 1px solid #000000; +} + +.blank-img { +border: 1px solid #000000; +background: #fcce32; +} + +.blank-img-ads { +border: 1px solid #000000; +background: #fcce32; +} + + +/* Content Section */ + +/*.content { +padding: 0 4px 4px 4px; +margin: 0; +}*/ + +.left-col { +border: 1px solid #000000; +background: #fcce32; +width: 145px; +} + +.left-nav { +margin: 3px; +} + +.statenav { + margin: 4px; +} + +.lnav-hdr { +background: #c5c6ac; +color: #000000; +font-family: verdana, tahoma, arial, sans-serif; +font-size: 11px; +font-weight: bold; +border: 1px solid #000000; +margin-top: 16px; +padding: 2px 5px; +} + +.left-nav p { +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +line-height: 14px; +margin:5px; +} + +.left-nav .access { +text-decoration : underline; +color : #000000; +background : inherit; +} + +a.lhdr:link { +color: #000000; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +font-weight: bold; +} +a.lhdr:visited { +color: #000000; +background : inherit; +text-decoration : none; +font-size: 10px; +font-weight: bold; +} +a.lhdr:hover { +color: #000; +background : #fcfefc; +text-decoration : underline; +} +a.lhdr:active { +color: #555; +background : inherit; +text-decoration : none; +} + +input.nobutton { + background-color: #0A50A1; + font-weight: bold; + font-size: 9px; + color: white; +} + +input.nobutton:visited { + background-color: #fcce32; + font-weight: bold; + font-size: 9px; + color: white; +} + +input.nobutton:active { + background-color: #fcce32 + color: white; + font-weight: bold; + font-size: 9px; +} + +a.nav:link { +color: #000099; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +} +a.nav:visited { +color: #000099; +background : inherit; +text-decoration : none; +} +a.nav:hover { +color: #000; +background : #fcfefc; +text-decoration : underline; +} +a.nav:active { +color: #999; +background : inherit; +text-decoration : none; +} + + + +/* Main content box */ +.content-box { +padding: 0; +margin: 0; +} + +/* right content box */ +.rhcol { +float: right; +width: 160px; +margin: 0 0 10px 15px; +background: #fcfefc; +color: #000000; +} +.rhcont { +border-left: 1px solid #000000; +border-bottom:3px solid #000000; +} +.rnav-hdr { +background: #c5c6ac; +color: #000000; +font-family: verdana, tahoma, arial, sans-serif; +font-size: 12px; +font-weight:bold; +border-bottom: 1px solid #000; +border-left: 2px solid #000000; +margin: 3px 0; +padding: 2px; +} + +/* main content area */ + +.content-title { +margin: 5px 0px 15px; +font-size: 14px; +text-decoration: underline; +} + +.bottomindent { +margin-bottom: 5px; +} + +.content-title-blog { +margin: 5px 0px 2px; +margin-left: 8px; +font-size: 16px; +text-decoration: none; +} + +.content-title-no1 { +margin: 5px 0px 15px; +font-size: 14px; +text-decoration: none; +} + +.content-title-mod { +margin: 5px 10px 15px; +font-size: 14px; +text-decoration: underline; +} + +.content-title-no { +margin: 0px 0px 0px; +font-size: 12px; +text-decoration: none; +} + +.content-box .cont { +margin: 10px 10px 15px 10px; +font-size: 11px; +line-height: 15px; +} + +.content-box .cont-list { +margin: 2px 10px 5px 10px; +} + +.content-box .sub-hdr { +margin: 2px 10px 10px; +font-size: 12px; +text-decoration: underline; +} + +.content-box .sub-hdr-mod { +margin: 2px 10px 10px; +font-size: 11px; +text-decoration: none; +} + +/* Bottom footer box */ + +.footer-col { +margin: 0 0 0 4px; +} + +.footer-box { +border: 1px solid #000000; +background:#343234; +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +color:#ffffff; +padding: 0 0 0 15px; +} + +.footer-box .cont1 { +margin: 8px 10px 5px 5px; +font-size: 11px; +line-height: 15px; +} +.footer-box .cont2 { +margin: 2px 10px 15px 5px; +font-size: 11px; +line-height: 15px; +} + +.footer-box input { +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +} + +/* Copyright layer */ + +.copyright { +background: #c5c6ac; +font-family: verdana, arial, tahoma, sans-serif; +font-size: 10px; +font-weight: bold; +padding: 6px; +border: solid #000000; +border-width: 1px 0 0; +} + +.rh-ad { +padding: 10px 0; +} + +/* Terms of use on front page */ +.tou { +margin: 0 0 0 10px; +} + + +/* for templates info */ +.temp-lh { +float : left; +width : 100px; +margin : 0; +padding : 2px 10px 2px 0; +text-align : left; +} +.temp-rh { +margin : 0; +padding : 2px 0; +text-align : left; +} +.date { +font-size : 10px; +color : #787d63; +background : inherit; +} +.temp-list ul { +font-size : 11px; +margin : 0; +padding : 0; +list-style-type : none; +} +.temp-list { +padding : 0 0 0 15px; +} + +/* small*/ +small.fsize { +font-size : 9px; +color : #999999; +background : inherit; +} + +.code { +color : #663300; +background : #eeeeee; +font-family : verdana, arial, sans-serif; +font-size : 11px; +line-height : 20px; +padding : 4px 10px; +margin : 10px 20px; +border : 1px dashed #999999; +} + + +/* Search form on front page */ + +.search-form { +color: #000; +background: #c5c6ac; +padding: 5px; +margin: 0px 0 10px 30px; +border: 1px solid #333333; +width:465px; +text-align: center; +white-space: nowrap; +} + +/* Search forms on Search page */ + +.adv-search { +margin: 0 0 0 15px; +} + +/* Tool Tip */ +.tip { +cursor : help; +border-bottom : 1px dashed #555555; +} + +/* ADVERTISEMENTS */ +/* container div is 'id=adspace' */ +#adspace { +background: inherit; +} +.ads { +background: inherit; +margin: 5px 0 15px 4px; +font-size: 11px; +padding: 5px; +border: solid #555; +border-width: 1px 0 0 1px; +} +.ads a:link { font-size: 11px; } diff --git a/site/roadshow/class/rating/styles/rating.css b/site/roadshow/class/rating/styles/rating.css new file mode 100644 index 0000000..eec6d79 --- /dev/null +++ b/site/roadshow/class/rating/styles/rating.css @@ -0,0 +1,102 @@ +/* star rating code - use lists because its more semantic */ +/* No javascript required */ +/* all the stars are contained in one matrix to solve rollover problems with delay */ +/* the background position is just shifted to reveal the correct image. */ +/* the images are 16px by 16px and the background position will be shifted in negative 16px increments */ +/* key: B=Blank : O=Orange : G = Green * / +/*..... The Matrix ....... */ +/* colours ....Background position */ +/* B B B B B - (0 0)*/ +/* G B B B B - (0 -16px)*/ +/* G G B B B - (0 -32px)*/ +/* G G G B B - (0 -48px)*/ +/* G G G G B - (0 -64px)*/ +/* G G G G G - (0 -80px)*/ +/* O B B B B - (0 -96px)*/ +/* O O B B B - (0 -112px)*/ +/* O O O B B - (0 -128px)*/ +/* O O O O B - (0 -144px)*/ +/* O O O O O - (0 -160px)*/ + + +/* the default rating is placed as a background image in the ul */ +/* use the background position according to the table above to display the required images*/ +.rating{ + width:80px; + height:16px; + margin:0px 0px 20px 0px; + padding:0; + list-style:none; + float: left; + position:relative; + background: url(../images/star-matrix.gif) no-repeat 0 0; +} +ul.rating li { + cursor: pointer; + /*ie5 mac doesn't like it if the list is floated\*/ + float:left; + /* end hide*/ + text-indent:-999em; +} +ul.rating li a { + position:absolute; + left:0; + top:0; + width:16px; + height:16px; + text-decoration:none; + z-index: 200; +} +ul.rating li.one a {left:0} +ul.rating li.two a {left:16px;} +ul.rating li.three a {left:32px;} +ul.rating li.four a {left:48px;} +ul.rating li.five a {left:64px;} +ul.rating li a:hover { + z-index:2; + width:80px; + height:16px; + overflow:hidden; + left:0; + background: url(../images/star-matrix.gif) no-repeat 0 0 +} +ul.rating li.one a:hover {background-position:0 -96px;} +ul.rating li.two a:hover {background-position:0 -112px;} +ul.rating li.three a:hover {background-position:0 -128px} +ul.rating li.four a:hover {background-position:0 -144px} +ul.rating li.five a:hover {background-position:0 -160px} + +/* this is used to remove the hover affect */ +/* use the background position according to the table above to display the required images*/ +.rated{ + width:80px; + height:16px; + margin:0px 0px 20px 0px; + padding:0; + list-style:none; + float: left; + position:relative; + background: url(../images/star-matrix.gif) no-repeat 0 0; +} +ul.rated li { + cursor: pointer; + /*ie5 mac doesn't like it if the list is floated\*/ + float:left; + /* end hide*/ + text-indent:-999em; +} +ul.rated li.one a {left:0} +ul.rated li.two a {left:16px;} +ul.rated li.three a {left:32px;} +ul.rated li.four a {left:48px;} +ul.rated li.five a {left:64px;} + +/* add these classes to the ul to effect the change to the correct number of stars */ +.nostar {background-position:0 0} +.onestar {background-position:0 -16px} +.twostar {background-position:0 -32px} +.threestar {background-position:0 -48px} +.fourstar {background-position:0 -64px} +.fivestar {background-position:0 -80px} +/* end rating code */ +h3{margin:0 0 2px 0;font-size:110%} \ No newline at end of file diff --git a/site/roadshow/rating/styles/rating.css b/site/roadshow/rating/styles/rating.css new file mode 100644 index 0000000..eec6d79 --- /dev/null +++ b/site/roadshow/rating/styles/rating.css @@ -0,0 +1,102 @@ +/* star rating code - use lists because its more semantic */ +/* No javascript required */ +/* all the stars are contained in one matrix to solve rollover problems with delay */ +/* the background position is just shifted to reveal the correct image. */ +/* the images are 16px by 16px and the background position will be shifted in negative 16px increments */ +/* key: B=Blank : O=Orange : G = Green * / +/*..... The Matrix ....... */ +/* colours ....Background position */ +/* B B B B B - (0 0)*/ +/* G B B B B - (0 -16px)*/ +/* G G B B B - (0 -32px)*/ +/* G G G B B - (0 -48px)*/ +/* G G G G B - (0 -64px)*/ +/* G G G G G - (0 -80px)*/ +/* O B B B B - (0 -96px)*/ +/* O O B B B - (0 -112px)*/ +/* O O O B B - (0 -128px)*/ +/* O O O O B - (0 -144px)*/ +/* O O O O O - (0 -160px)*/ + + +/* the default rating is placed as a background image in the ul */ +/* use the background position according to the table above to display the required images*/ +.rating{ + width:80px; + height:16px; + margin:0px 0px 20px 0px; + padding:0; + list-style:none; + float: left; + position:relative; + background: url(../images/star-matrix.gif) no-repeat 0 0; +} +ul.rating li { + cursor: pointer; + /*ie5 mac doesn't like it if the list is floated\*/ + float:left; + /* end hide*/ + text-indent:-999em; +} +ul.rating li a { + position:absolute; + left:0; + top:0; + width:16px; + height:16px; + text-decoration:none; + z-index: 200; +} +ul.rating li.one a {left:0} +ul.rating li.two a {left:16px;} +ul.rating li.three a {left:32px;} +ul.rating li.four a {left:48px;} +ul.rating li.five a {left:64px;} +ul.rating li a:hover { + z-index:2; + width:80px; + height:16px; + overflow:hidden; + left:0; + background: url(../images/star-matrix.gif) no-repeat 0 0 +} +ul.rating li.one a:hover {background-position:0 -96px;} +ul.rating li.two a:hover {background-position:0 -112px;} +ul.rating li.three a:hover {background-position:0 -128px} +ul.rating li.four a:hover {background-position:0 -144px} +ul.rating li.five a:hover {background-position:0 -160px} + +/* this is used to remove the hover affect */ +/* use the background position according to the table above to display the required images*/ +.rated{ + width:80px; + height:16px; + margin:0px 0px 20px 0px; + padding:0; + list-style:none; + float: left; + position:relative; + background: url(../images/star-matrix.gif) no-repeat 0 0; +} +ul.rated li { + cursor: pointer; + /*ie5 mac doesn't like it if the list is floated\*/ + float:left; + /* end hide*/ + text-indent:-999em; +} +ul.rated li.one a {left:0} +ul.rated li.two a {left:16px;} +ul.rated li.three a {left:32px;} +ul.rated li.four a {left:48px;} +ul.rated li.five a {left:64px;} + +/* add these classes to the ul to effect the change to the correct number of stars */ +.nostar {background-position:0 0} +.onestar {background-position:0 -16px} +.twostar {background-position:0 -32px} +.threestar {background-position:0 -48px} +.fourstar {background-position:0 -64px} +.fivestar {background-position:0 -80px} +/* end rating code */ +h3{margin:0 0 2px 0;font-size:110%} \ No newline at end of file diff --git a/site/roadshow/styles.css b/site/roadshow/styles.css new file mode 100644 index 0000000..d2bd5d1 --- /dev/null +++ b/site/roadshow/styles.css @@ -0,0 +1,545 @@ +/* Defaults */ +body { +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +color: #000; +padding: 0; +margin: 15px 0 5px; +} + +.indent { +margin-left: 10px; +} + +.indentlarge { +margin-left: 30px; +} + +.indenthuge { +margin-left: 50px; +} + +.rightside { +text-align: right; +} + +form { +margin: 0; +padding: 0; +} + +/* Links +a:link { +color: #000099; +text-decoration : none; +padding : 0; +margin : 0; +} +a:visited { +color: #000099; +text-decoration : none; +} +a:hover { +color: #000000; +text-decoration : underline; +} +a:active { +color: #999999; +text-decoration : none; +}*/ + +.modify{ +text-decoration: none; +color: #FFFFFF; +font-weight: bold; +font-size: 14pt; +} + +.modify:hover{ +text-decoration: underline; +} + +/* Outer layer */ + +#outer { +text-align: left; +background: #ffffff; +color: inherit; +/**border: 1px solid #000000;**/ +margin: 0 auto; +padding: 0; +} + +/* Header */ + +.header { +background: #fcce32; +} + +.logolink { +padding: 6px 3px; +} + + .logolink a { +background: inherit; +} + +.hdr-links { +width: 230px; +text-align: center; +font-size : 10px; +padding : 0; +margin: 0; +} + +a.hdrnav:link { +color: #000099; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +} +a.hdrnav:visited { +color: #000099; +background : inherit; +text-decoration : none; +} +a.hdrnav:hover { +color: #000; +background : #fff; +text-decoration : underline; +} +a.hdrnav:active { +color: #999; +background : inherit; +text-decoration : none; +} + +.hdr-links .access { +text-decoration : underline; +color : #000; +background : inherit; +} + + +/* Top Nav Bar */ + +.top-nav { +height: 25px; +background: #c5c6ac; +color: inherit; +border: solid #000000; +border-width: 1px 0; +} +.top-nav-inner { +padding: 5px 10px; +} +.top-nav-inner a:link { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: none; +} +.top-nav-inner a:visited { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: none; +} +.top-nav-inner a:hover { +background: inherit; +color: #555555; +font-weight: bold; +text-decoration: underline; +} +.top-nav-inner a:active { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: underline; +} + + + +/* Image Bar */ + +.image-bar { +clear:both; +} + +.title-img { +border: 1px solid #000; +color: #ffffff; +background: #343234; +padding: 0 0 0 10px; +} + +.section-title { +font-size: 16px; +font-weight: bold; +font-family: verdana, arial, tahoma, sans-serif; +} + +.graphic-img { +border: 1px solid #000000; +} + +.blank-img { +border: 1px solid #000000; +background: #fcce32; +} + +.blank-img-ads { +border: 1px solid #000000; +background: #fcce32; +} + + +/* Content Section */ + +/*.content { +padding: 0 4px 4px 4px; +margin: 0; +}*/ + +.left-col { +border: 1px solid #000000; +background: #fcce32; +width: 145px; +} + +.left-nav { +margin: 3px; +} + +.statenav { + margin: 4px; +} + +.lnav-hdr { +background: #c5c6ac; +color: #000000; +font-family: verdana, tahoma, arial, sans-serif; +font-size: 11px; +font-weight: bold; +border: 1px solid #000000; +margin-top: 16px; +padding: 2px 5px; +} + +.left-nav p { +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +line-height: 14px; +margin:5px; +} + +.left-nav .access { +text-decoration : underline; +color : #000000; +background : inherit; +} + +a.lhdr:link { +color: #000000; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +font-weight: bold; +} +a.lhdr:visited { +color: #000000; +background : inherit; +text-decoration : none; +font-size: 10px; +font-weight: bold; +} +a.lhdr:hover { +color: #000; +background : #fcfefc; +text-decoration : underline; +} +a.lhdr:active { +color: #555; +background : inherit; +text-decoration : none; +} + +input.nobutton { + background-color: #0A50A1; + font-weight: bold; + font-size: 9px; + color: white; +} + +input.nobutton:visited { + background-color: #fcce32; + font-weight: bold; + font-size: 9px; + color: white; +} + +input.nobutton:active { + background-color: #fcce32 + color: white; + font-weight: bold; + font-size: 9px; +} + +a.nav:link { +color: #000099; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +} +a.nav:visited { +color: #000099; +background : inherit; +text-decoration : none; +} +a.nav:hover { +color: #000; +background : #fcfefc; +text-decoration : underline; +} +a.nav:active { +color: #999; +background : inherit; +text-decoration : none; +} + + + +/* Main content box */ +.content-box { +padding: 0; +margin: 0; +} + +/* right content box */ +.rhcol { +float: right; +width: 160px; +margin: 0 0 10px 15px; +background: #fcfefc; +color: #000000; +} +.rhcont { +border-left: 1px solid #000000; +border-bottom:3px solid #000000; +} +.rnav-hdr { +background: #c5c6ac; +color: #000000; +font-family: verdana, tahoma, arial, sans-serif; +font-size: 12px; +font-weight:bold; +border-bottom: 1px solid #000; +border-left: 2px solid #000000; +margin: 3px 0; +padding: 2px; +} + +/* main content area */ + +.content-title { +margin: 5px 0px 15px; +font-size: 14px; +text-decoration: underline; +} + +.bottomindent { +margin-bottom: 5px; +} + +.content-title-blog { +margin: 5px 0px 2px; +margin-left: 8px; +font-size: 16px; +text-decoration: none; +} + +.content-title-no1 { +margin: 5px 0px 15px; +font-size: 14px; +text-decoration: none; +} + +.content-title-mod { +margin: 5px 10px 15px; +font-size: 14px; +text-decoration: underline; +} + +.content-title-no { +margin: 0px 0px 0px; +font-size: 12px; +text-decoration: none; +} + +.content-box .cont { +margin: 10px 10px 15px 10px; +font-size: 11px; +line-height: 15px; +} + +.content-box .cont-list { +margin: 2px 10px 5px 10px; +} + +.content-box .sub-hdr { +margin: 2px 10px 10px; +font-size: 12px; +text-decoration: underline; +} + +.content-box .sub-hdr-mod { +margin: 2px 10px 10px; +font-size: 11px; +text-decoration: none; +} + +/* Bottom footer box */ + +.footer-col { +margin: 0 0 0 4px; +} + +.footer-box { +border: 1px solid #000000; +background:#343234; +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +color:#ffffff; +padding: 0 0 0 15px; +} + +.footer-box .cont1 { +margin: 8px 10px 5px 5px; +font-size: 11px; +line-height: 15px; +} +.footer-box .cont2 { +margin: 2px 10px 15px 5px; +font-size: 11px; +line-height: 15px; +} + +.footer-box input { +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +} + +/* Copyright layer */ + +.copyright { +background: #c5c6ac; +font-family: verdana, arial, tahoma, sans-serif; +font-size: 10px; +font-weight: bold; +padding: 6px; +border: solid #000000; +border-width: 1px 0 0; +} + +.rh-ad { +padding: 10px 0; +} + +/* Terms of use on front page */ +.tou { +margin: 0 0 0 10px; +} + + +/* for templates info */ +.temp-lh { +float : left; +width : 100px; +margin : 0; +padding : 2px 10px 2px 0; +text-align : left; +} +.temp-rh { +margin : 0; +padding : 2px 0; +text-align : left; +} +.date { +font-size : 10px; +color : #787d63; +background : inherit; +} +.temp-list ul { +font-size : 11px; +margin : 0; +padding : 0; +list-style-type : none; +} +.temp-list { +padding : 0 0 0 15px; +} + +/* small*/ +small.fsize { +font-size : 9px; +color : #999999; +background : inherit; +} + +.code { +color : #663300; +background : #eeeeee; +font-family : verdana, arial, sans-serif; +font-size : 11px; +line-height : 20px; +padding : 4px 10px; +margin : 10px 20px; +border : 1px dashed #999999; +} + + +/* Search form on front page */ + +.search-form { +color: #000; +background: #c5c6ac; +padding: 5px; +margin: 0px 0 10px 30px; +border: 1px solid #333333; +width:465px; +text-align: center; +white-space: nowrap; +} + +/* Search forms on Search page */ + +.adv-search { +margin: 0 0 0 15px; +} + +/* Tool Tip */ +.tip { +cursor : help; +border-bottom : 1px dashed #555555; +} + +/* ADVERTISEMENTS */ +/* container div is 'id=adspace' */ +#adspace { +background: inherit; +} +.ads { +background: inherit; +margin: 5px 0 15px 4px; +font-size: 11px; +padding: 5px; +border: solid #555; +border-width: 1px 0 0 1px; +} +.ads a:link { font-size: 11px; } diff --git a/site/runshops/extlib/Snoopy.class.inc b/site/runshops/extlib/Snoopy.class.inc new file mode 100644 index 0000000..22dc434 --- /dev/null +++ b/site/runshops/extlib/Snoopy.class.inc @@ -0,0 +1,901 @@ + +Copyright (c): 1999-2000 ispi, all rights reserved +Version: 1.0 + + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +You may contact the author of Snoopy by e-mail at: +monte@ispi.net + +Or, write to: +Monte Ohrt +CTO, ispi +237 S. 70th suite 220 +Lincoln, NE 68510 + +The latest version of Snoopy can be obtained from: +http://snoopy.sourceforge.com + +*************************************************/ + +class Snoopy +{ + /**** Public variables ****/ + + /* user definable vars */ + + var $host = "www.php.net"; // host name we are connecting to + var $port = 80; // port we are connecting to + var $proxy_host = ""; // proxy host to use + var $proxy_port = ""; // proxy port to use + var $agent = "Snoopy v1.0"; // agent we masquerade as + var $referer = ""; // referer info to pass + var $cookies = array(); // array of cookies to pass + // $cookies["username"]="joe"; + var $rawheaders = array(); // array of raw headers to send + // $rawheaders["Content-type"]="text/html"; + + var $maxredirs = 5; // http redirection depth maximum. 0 = disallow + var $lastredirectaddr = ""; // contains address of last redirected address + var $offsiteok = true; // allows redirection off-site + var $maxframes = 0; // frame content depth maximum. 0 = disallow + var $expandlinks = true; // expand links to fully qualified URLs. + // this only applies to fetchlinks() + // or submitlinks() + var $passcookies = true; // pass set cookies back through redirects + // NOTE: this currently does not respect + // dates, domains or paths. + + var $user = ""; // user for http authentication + var $pass = ""; // password for http authentication + + // http accept types + var $accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*"; + + var $results = ""; // where the content is put + + var $error = ""; // error messages sent here + var $response_code = ""; // response code returned from server + var $headers = array(); // headers returned from server sent here + var $maxlength = 500000; // max return data length (body) + var $read_timeout = 0; // timeout on read operations, in seconds + // supported only since PHP 4 Beta 4 + // set to 0 to disallow timeouts + var $timed_out = false; // if a read operation timed out + var $status = 0; // http request status + + var $curl_path = "/usr/bin/curl"; + // Snoopy will use cURL for fetching + // SSL content if a full system path to + // the cURL binary is supplied here. + // set to false if you do not have + // cURL installed. See http://curl.haxx.se + // for details on installing cURL. + // Snoopy does *not* use the cURL + // library functions built into php, + // as these functions are not stable + // as of this Snoopy release. + + // send Accept-encoding: gzip? + var $use_gzip = true; + + /**** Private variables ****/ + + var $_maxlinelen = 4096; // max line length (headers) + + var $_httpmethod = "GET"; // default http request method + var $_httpversion = "HTTP/1.0"; // default http request version + var $_submit_method = "POST"; // default submit method + var $_submit_type = "application/x-www-form-urlencoded"; // default submit type + var $_mime_boundary = ""; // MIME boundary for multipart/form-data submit type + var $_redirectaddr = false; // will be set if page fetched is a redirect + var $_redirectdepth = 0; // increments on an http redirect + var $_frameurls = array(); // frame src urls + var $_framedepth = 0; // increments on frame depth + + var $_isproxy = false; // set if using a proxy server + var $_fp_timeout = 30; // timeout for socket connection + +/*======================================================================*\ + Function: fetch + Purpose: fetch the contents of a web page + (and possibly other protocols in the + future like ftp, nntp, gopher, etc.) + Input: $URI the location of the page to fetch + Output: $this->results the output text from the fetch +\*======================================================================*/ + + function fetch($URI) + { + + //preg_match("|^([^:]+)://([^:/]+)(:[\d]+)*(.*)|",$URI,$URI_PARTS); + $URI_PARTS = parse_url($URI); + if (!empty($URI_PARTS["user"])) + $this->user = $URI_PARTS["user"]; + if (!empty($URI_PARTS["pass"])) + $this->pass = $URI_PARTS["pass"]; + + switch($URI_PARTS["scheme"]) + { + case "http": + $this->host = $URI_PARTS["host"]; + if(!empty($URI_PARTS["port"])) + $this->port = $URI_PARTS["port"]; + if($this->_connect($fp)) + { + if($this->_isproxy) + { + // using proxy, send entire URI + $this->_httprequest($URI,$fp,$URI,$this->_httpmethod); + } + else + { + $path = $URI_PARTS["path"].(isset($URI_PARTS["query"]) ? "?".$URI_PARTS["query"] : ""); + // no proxy, send only the path + $this->_httprequest($path, $fp, $URI, $this->_httpmethod); + } + + $this->_disconnect($fp); + + if($this->_redirectaddr) + { + /* url was redirected, check if we've hit the max depth */ + if($this->maxredirs > $this->_redirectdepth) + { + // only follow redirect if it's on this site, or offsiteok is true + if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok) + { + /* follow the redirect */ + $this->_redirectdepth++; + $this->lastredirectaddr=$this->_redirectaddr; + $this->fetch($this->_redirectaddr); + } + } + } + + if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0) + { + $frameurls = $this->_frameurls; + $this->_frameurls = array(); + + while(list(,$frameurl) = each($frameurls)) + { + if($this->_framedepth < $this->maxframes) + { + $this->fetch($frameurl); + $this->_framedepth++; + } + else + break; + } + } + } + else + { + return false; + } + return true; + break; + case "https": + if(!$this->curl_path || (!is_executable($this->curl_path))) { + $this->error = "Bad curl ($this->curl_path), can't fetch HTTPS \n"; + return false; + } + $this->host = $URI_PARTS["host"]; + if(!empty($URI_PARTS["port"])) + $this->port = $URI_PARTS["port"]; + if($this->_isproxy) + { + // using proxy, send entire URI + $this->_httpsrequest($URI,$URI,$this->_httpmethod); + } + else + { + $path = $URI_PARTS["path"].($URI_PARTS["query"] ? "?".$URI_PARTS["query"] : ""); + // no proxy, send only the path + $this->_httpsrequest($path, $URI, $this->_httpmethod); + } + + if($this->_redirectaddr) + { + /* url was redirected, check if we've hit the max depth */ + if($this->maxredirs > $this->_redirectdepth) + { + // only follow redirect if it's on this site, or offsiteok is true + if(preg_match("|^http://".preg_quote($this->host)."|i",$this->_redirectaddr) || $this->offsiteok) + { + /* follow the redirect */ + $this->_redirectdepth++; + $this->lastredirectaddr=$this->_redirectaddr; + $this->fetch($this->_redirectaddr); + } + } + } + + if($this->_framedepth < $this->maxframes && count($this->_frameurls) > 0) + { + $frameurls = $this->_frameurls; + $this->_frameurls = array(); + + while(list(,$frameurl) = each($frameurls)) + { + if($this->_framedepth < $this->maxframes) + { + $this->fetch($frameurl); + $this->_framedepth++; + } + else + break; + } + } + return true; + break; + default: + // not a valid protocol + $this->error = 'Invalid protocol "'.$URI_PARTS["scheme"].'"\n'; + return false; + break; + } + return true; + } + + + +/*======================================================================*\ + Private functions +\*======================================================================*/ + + +/*======================================================================*\ + Function: _striplinks + Purpose: strip the hyperlinks from an html document + Input: $document document to strip. + Output: $match an array of the links +\*======================================================================*/ + + function _striplinks($document) + { + preg_match_all("'<\s*a\s+.*href\s*=\s* # find ]+)) # if quote found, match up to next matching + # quote, otherwise match up to next space + 'isx",$document,$links); + + + // catenate the non-empty matches from the conditional subpattern + + while(list($key,$val) = each($links[2])) + { + if(!empty($val)) + $match[] = $val; + } + + while(list($key,$val) = each($links[3])) + { + if(!empty($val)) + $match[] = $val; + } + + // return the links + return $match; + } + +/*======================================================================*\ + Function: _stripform + Purpose: strip the form elements from an html document + Input: $document document to strip. + Output: $match an array of the links +\*======================================================================*/ + + function _stripform($document) + { + preg_match_all("'<\/?(FORM|INPUT|SELECT|TEXTAREA|(OPTION))[^<>]*>(?(2)(.*(?=<\/?(option|select)[^<>]*>[\r\n]*)|(?=[\r\n]*))|(?=[\r\n]*))'Usi",$document,$elements); + + // catenate the matches + $match = implode("\r\n",$elements[0]); + + // return the links + return $match; + } + + + +/*======================================================================*\ + Function: _striptext + Purpose: strip the text from an html document + Input: $document document to strip. + Output: $text the resulting text +\*======================================================================*/ + + function _striptext($document) + { + + // I didn't use preg eval (//e) since that is only available in PHP 4.0. + // so, list your entities one by one here. I included some of the + // more common ones. + + $search = array("']*?>.*?'si", // strip out javascript + "'<[\/\!]*?[^<>]*?>'si", // strip out html tags + "'([\r\n])[\s]+'", // strip out white space + "'&(quote|#34);'i", // replace html entities + "'&(amp|#38);'i", + "'&(lt|#60);'i", + "'&(gt|#62);'i", + "'&(nbsp|#160);'i", + "'&(iexcl|#161);'i", + "'&(cent|#162);'i", + "'&(pound|#163);'i", + "'&(copy|#169);'i" + ); + $replace = array( "", + "", + "\\1", + "\"", + "&", + "<", + ">", + " ", + chr(161), + chr(162), + chr(163), + chr(169)); + + $text = preg_replace($search,$replace,$document); + + return $text; + } + +/*======================================================================*\ + Function: _expandlinks + Purpose: expand each link into a fully qualified URL + Input: $links the links to qualify + $URI the full URI to get the base from + Output: $expandedLinks the expanded links +\*======================================================================*/ + + function _expandlinks($links,$URI) + { + + preg_match("/^[^\?]+/",$URI,$match); + + $match = preg_replace("|/[^\/\.]+\.[^\/\.]+$|","",$match[0]); + + $search = array( "|^http://".preg_quote($this->host)."|i", + "|^(?!http://)(\/)?(?!mailto:)|i", + "|/\./|", + "|/[^\/]+/\.\./|" + ); + + $replace = array( "", + $match."/", + "/", + "/" + ); + + $expandedLinks = preg_replace($search,$replace,$links); + + return $expandedLinks; + } + +/*======================================================================*\ + Function: _httprequest + Purpose: go get the http data from the server + Input: $url the url to fetch + $fp the current open file pointer + $URI the full URI + $body body contents to send if any (POST) + Output: +\*======================================================================*/ + + function _httprequest($url,$fp,$URI,$http_method,$content_type="",$body="") + { + if($this->passcookies && $this->_redirectaddr) + $this->setcookies(); + + $URI_PARTS = parse_url($URI); + if(empty($url)) + $url = "/"; + $headers = $http_method." ".$url." ".$this->_httpversion."\r\n"; + if(!empty($this->agent)) + $headers .= "User-Agent: ".$this->agent."\r\n"; + if(!empty($this->host) && !isset($this->rawheaders['Host'])) + $headers .= "Host: ".$this->host."\r\n"; + if(!empty($this->accept)) + $headers .= "Accept: ".$this->accept."\r\n"; + + if($this->use_gzip) { + // make sure PHP was built with --with-zlib + // and we can handle gzipp'ed data + if ( function_exists(gzinflate) ) { + $headers .= "Accept-encoding: gzip\r\n"; + } + else { + trigger_error( + "use_gzip is on, but PHP was built without zlib support.". + " Requesting file(s) without gzip encoding.", + E_USER_NOTICE); + } + } + + if(!empty($this->referer)) + $headers .= "Referer: ".$this->referer."\r\n"; + if(!empty($this->cookies)) + { + if(!is_array($this->cookies)) + $this->cookies = (array)$this->cookies; + + reset($this->cookies); + if ( count($this->cookies) > 0 ) { + $cookie_headers .= 'Cookie: '; + foreach ( $this->cookies as $cookieKey => $cookieVal ) { + $cookie_headers .= $cookieKey."=".urlencode($cookieVal)."; "; + } + $headers .= substr($cookie_headers,0,-2) . "\r\n"; + } + } + if(!empty($this->rawheaders)) + { + if(!is_array($this->rawheaders)) + $this->rawheaders = (array)$this->rawheaders; + while(list($headerKey,$headerVal) = each($this->rawheaders)) + $headers .= $headerKey.": ".$headerVal."\r\n"; + } + if(!empty($content_type)) { + $headers .= "Content-type: $content_type"; + if ($content_type == "multipart/form-data") + $headers .= "; boundary=".$this->_mime_boundary; + $headers .= "\r\n"; + } + if(!empty($body)) + $headers .= "Content-length: ".strlen($body)."\r\n"; + if(!empty($this->user) || !empty($this->pass)) + $headers .= "Authorization: BASIC ".base64_encode($this->user.":".$this->pass)."\r\n"; + + $headers .= "\r\n"; + + // set the read timeout if needed + if ($this->read_timeout > 0) + socket_set_timeout($fp, $this->read_timeout); + $this->timed_out = false; + + fwrite($fp,$headers.$body,strlen($headers.$body)); + + $this->_redirectaddr = false; + unset($this->headers); + + // content was returned gzip encoded? + $is_gzipped = false; + + while($currentHeader = fgets($fp,$this->_maxlinelen)) + { + if ($this->read_timeout > 0 && $this->_check_timeout($fp)) + { + $this->status=-100; + return false; + } + + // if($currentHeader == "\r\n") + if(preg_match("/^\r?\n$/", $currentHeader) ) + break; + + // if a header begins with Location: or URI:, set the redirect + if(preg_match("/^(Location:|URI:)/i",$currentHeader)) + { + // get URL portion of the redirect + preg_match("/^(Location:|URI:)\s+(.*)/",chop($currentHeader),$matches); + // look for :// in the Location header to see if hostname is included + if(!preg_match("|\:\/\/|",$matches[2])) + { + // no host in the path, so prepend + $this->_redirectaddr = $URI_PARTS["scheme"]."://".$this->host.":".$this->port; + // eliminate double slash + if(!preg_match("|^/|",$matches[2])) + $this->_redirectaddr .= "/".$matches[2]; + else + $this->_redirectaddr .= $matches[2]; + } + else + $this->_redirectaddr = $matches[2]; + } + + if(preg_match("|^HTTP/|",$currentHeader)) + { + if(preg_match("|^HTTP/[^\s]*\s(.*?)\s|",$currentHeader, $status)) + { + $this->status= $status[1]; + } + $this->response_code = $currentHeader; + } + + if (preg_match("/Content-Encoding: gzip/", $currentHeader) ) { + $is_gzipped = true; + } + + $this->headers[] = $currentHeader; + } + + # $results = fread($fp, $this->maxlength); + $results = ""; + while ( $data = fread($fp, $this->maxlength) ) { + $results .= $data; + if ( + strlen($results) > $this->maxlength ) { + break; + } + } + + // gunzip + if ( $is_gzipped ) { + // per http://www.php.net/manual/en/function.gzencode.php + $results = substr($results, 10); + $results = gzinflate($results); + } + + if ($this->read_timeout > 0 && $this->_check_timeout($fp)) + { + $this->status=-100; + return false; + } + + // check if there is a a redirect meta tag + + if(preg_match("']*?content[\s]*=[\s]*[\"\']?\d+;[\s]+URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i",$results,$match)) + { + $this->_redirectaddr = $this->_expandlinks($match[1],$URI); + } + + // have we hit our frame depth and is there frame src to fetch? + if(($this->_framedepth < $this->maxframes) && preg_match_all("']+)'i",$results,$match)) + { + $this->results[] = $results; + for($x=0; $x_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS["scheme"]."://".$this->host); + } + // have we already fetched framed content? + elseif(is_array($this->results)) + $this->results[] = $results; + // no framed content + else + $this->results = $results; + + return true; + } + +/*======================================================================*\ + Function: _httpsrequest + Purpose: go get the https data from the server using curl + Input: $url the url to fetch + $URI the full URI + $body body contents to send if any (POST) + Output: +\*======================================================================*/ + + function _httpsrequest($url,$URI,$http_method,$content_type="",$body="") + { + if($this->passcookies && $this->_redirectaddr) + $this->setcookies(); + + $headers = array(); + + $URI_PARTS = parse_url($URI); + if(empty($url)) + $url = "/"; + // GET ... header not needed for curl + //$headers[] = $http_method." ".$url." ".$this->_httpversion; + if(!empty($this->agent)) + $headers[] = "User-Agent: ".$this->agent; + if(!empty($this->host)) + $headers[] = "Host: ".$this->host; + if(!empty($this->accept)) + $headers[] = "Accept: ".$this->accept; + if(!empty($this->referer)) + $headers[] = "Referer: ".$this->referer; + if(!empty($this->cookies)) + { + if(!is_array($this->cookies)) + $this->cookies = (array)$this->cookies; + + reset($this->cookies); + if ( count($this->cookies) > 0 ) { + $cookie_str = 'Cookie: '; + foreach ( $this->cookies as $cookieKey => $cookieVal ) { + $cookie_str .= $cookieKey."=".urlencode($cookieVal)."; "; + } + $headers[] = substr($cookie_str,0,-2); + } + } + if(!empty($this->rawheaders)) + { + if(!is_array($this->rawheaders)) + $this->rawheaders = (array)$this->rawheaders; + while(list($headerKey,$headerVal) = each($this->rawheaders)) + $headers[] = $headerKey.": ".$headerVal; + } + if(!empty($content_type)) { + if ($content_type == "multipart/form-data") + $headers[] = "Content-type: $content_type; boundary=".$this->_mime_boundary; + else + $headers[] = "Content-type: $content_type"; + } + if(!empty($body)) + $headers[] = "Content-length: ".strlen($body); + if(!empty($this->user) || !empty($this->pass)) + $headers[] = "Authorization: BASIC ".base64_encode($this->user.":".$this->pass); + + for($curr_header = 0; $curr_header < count($headers); $curr_header++) { + $cmdline_params .= " -H \"".$headers[$curr_header]."\""; + } + + if(!empty($body)) + $cmdline_params .= " -d \"$body\""; + + if($this->read_timeout > 0) + $cmdline_params .= " -m ".$this->read_timeout; + + $headerfile = uniqid(time()); + + # accept self-signed certs + $cmdline_params .= " -k"; + exec($this->curl_path." -D \"/tmp/$headerfile\"".escapeshellcmd($cmdline_params)." ".escapeshellcmd($URI),$results,$return); + + if($return) + { + $this->error = "Error: cURL could not retrieve the document, error $return."; + return false; + } + + + $results = implode("\r\n",$results); + + $result_headers = file("/tmp/$headerfile"); + + $this->_redirectaddr = false; + unset($this->headers); + + for($currentHeader = 0; $currentHeader < count($result_headers); $currentHeader++) + { + + // if a header begins with Location: or URI:, set the redirect + if(preg_match("/^(Location: |URI: )/i",$result_headers[$currentHeader])) + { + // get URL portion of the redirect + preg_match("/^(Location: |URI:)(.*)/",chop($result_headers[$currentHeader]),$matches); + // look for :// in the Location header to see if hostname is included + if(!preg_match("|\:\/\/|",$matches[2])) + { + // no host in the path, so prepend + $this->_redirectaddr = $URI_PARTS["scheme"]."://".$this->host.":".$this->port; + // eliminate double slash + if(!preg_match("|^/|",$matches[2])) + $this->_redirectaddr .= "/".$matches[2]; + else + $this->_redirectaddr .= $matches[2]; + } + else + $this->_redirectaddr = $matches[2]; + } + + if(preg_match("|^HTTP/|",$result_headers[$currentHeader])) + { + $this->response_code = $result_headers[$currentHeader]; + if(preg_match("|^HTTP/[^\s]*\s(.*?)\s|",$this->response_code, $match)) + { + $this->status= $match[1]; + } + } + $this->headers[] = $result_headers[$currentHeader]; + } + + // check if there is a a redirect meta tag + + if(preg_match("']*?content[\s]*=[\s]*[\"\']?\d+;[\s]+URL[\s]*=[\s]*([^\"\']*?)[\"\']?>'i",$results,$match)) + { + $this->_redirectaddr = $this->_expandlinks($match[1],$URI); + } + + // have we hit our frame depth and is there frame src to fetch? + if(($this->_framedepth < $this->maxframes) && preg_match_all("']+)'i",$results,$match)) + { + $this->results[] = $results; + for($x=0; $x_frameurls[] = $this->_expandlinks($match[1][$x],$URI_PARTS["scheme"]."://".$this->host); + } + // have we already fetched framed content? + elseif(is_array($this->results)) + $this->results[] = $results; + // no framed content + else + $this->results = $results; + + unlink("/tmp/$headerfile"); + + return true; + } + +/*======================================================================*\ + Function: setcookies() + Purpose: set cookies for a redirection +\*======================================================================*/ + + function setcookies() + { + for($x=0; $xheaders); $x++) + { + if(preg_match("/^set-cookie:[\s]+([^=]+)=([^;]+)/i", $this->headers[$x],$match)) + $this->cookies[$match[1]] = $match[2]; + } + } + + +/*======================================================================*\ + Function: _check_timeout + Purpose: checks whether timeout has occurred + Input: $fp file pointer +\*======================================================================*/ + + function _check_timeout($fp) + { + if ($this->read_timeout > 0) { + $fp_status = socket_get_status($fp); + if ($fp_status["timed_out"]) { + $this->timed_out = true; + return true; + } + } + return false; + } + +/*======================================================================*\ + Function: _connect + Purpose: make a socket connection + Input: $fp file pointer +\*======================================================================*/ + + function _connect(&$fp) + { + if(!empty($this->proxy_host) && !empty($this->proxy_port)) + { + $this->_isproxy = true; + $host = $this->proxy_host; + $port = $this->proxy_port; + } + else + { + $host = $this->host; + $port = $this->port; + } + + $this->status = 0; + + if($fp = fsockopen( + $host, + $port, + $errno, + $errstr, + $this->_fp_timeout + )) + { + // socket connection succeeded + + return true; + } + else + { + // socket connection failed + $this->status = $errno; + switch($errno) + { + case -3: + $this->error="socket creation failed (-3)"; + case -4: + $this->error="dns lookup failure (-4)"; + case -5: + $this->error="connection refused or timed out (-5)"; + default: + $this->error="connection failed (".$errno.")"; + } + return false; + } + } +/*======================================================================*\ + Function: _disconnect + Purpose: disconnect a socket connection + Input: $fp file pointer +\*======================================================================*/ + + function _disconnect($fp) + { + return(fclose($fp)); + } + + +/*======================================================================*\ + Function: _prepare_post_body + Purpose: Prepare post body according to encoding type + Input: $formvars - form variables + $formfiles - form upload files + Output: post body +\*======================================================================*/ + + function _prepare_post_body($formvars, $formfiles) + { + settype($formvars, "array"); + settype($formfiles, "array"); + + if (count($formvars) == 0 && count($formfiles) == 0) + return; + + switch ($this->_submit_type) { + case "application/x-www-form-urlencoded": + reset($formvars); + while(list($key,$val) = each($formvars)) { + if (is_array($val) || is_object($val)) { + while (list($cur_key, $cur_val) = each($val)) { + $postdata .= urlencode($key)."[]=".urlencode($cur_val)."&"; + } + } else + $postdata .= urlencode($key)."=".urlencode($val)."&"; + } + break; + + case "multipart/form-data": + $this->_mime_boundary = "Snoopy".md5(uniqid(microtime())); + + reset($formvars); + while(list($key,$val) = each($formvars)) { + if (is_array($val) || is_object($val)) { + while (list($cur_key, $cur_val) = each($val)) { + $postdata .= "--".$this->_mime_boundary."\r\n"; + $postdata .= "Content-Disposition: form-data; name=\"$key\[\]\"\r\n\r\n"; + $postdata .= "$cur_val\r\n"; + } + } else { + $postdata .= "--".$this->_mime_boundary."\r\n"; + $postdata .= "Content-Disposition: form-data; name=\"$key\"\r\n\r\n"; + $postdata .= "$val\r\n"; + } + } + + reset($formfiles); + while (list($field_name, $file_names) = each($formfiles)) { + settype($file_names, "array"); + while (list(, $file_name) = each($file_names)) { + if (!is_readable($file_name)) continue; + + $fp = fopen($file_name, "r"); + $file_content = fread($fp, filesize($file_name)); + fclose($fp); + $base_name = basename($file_name); + + $postdata .= "--".$this->_mime_boundary."\r\n"; + $postdata .= "Content-Disposition: form-data; name=\"$field_name\"; filename=\"$base_name\"\r\n\r\n"; + $postdata .= "$file_content\r\n"; + } + } + $postdata .= "--".$this->_mime_boundary."--\r\n"; + break; + } + + return $postdata; + } +} + +?> + diff --git a/site/runshops/rating/styles/rating.css b/site/runshops/rating/styles/rating.css new file mode 100644 index 0000000..eec6d79 --- /dev/null +++ b/site/runshops/rating/styles/rating.css @@ -0,0 +1,102 @@ +/* star rating code - use lists because its more semantic */ +/* No javascript required */ +/* all the stars are contained in one matrix to solve rollover problems with delay */ +/* the background position is just shifted to reveal the correct image. */ +/* the images are 16px by 16px and the background position will be shifted in negative 16px increments */ +/* key: B=Blank : O=Orange : G = Green * / +/*..... The Matrix ....... */ +/* colours ....Background position */ +/* B B B B B - (0 0)*/ +/* G B B B B - (0 -16px)*/ +/* G G B B B - (0 -32px)*/ +/* G G G B B - (0 -48px)*/ +/* G G G G B - (0 -64px)*/ +/* G G G G G - (0 -80px)*/ +/* O B B B B - (0 -96px)*/ +/* O O B B B - (0 -112px)*/ +/* O O O B B - (0 -128px)*/ +/* O O O O B - (0 -144px)*/ +/* O O O O O - (0 -160px)*/ + + +/* the default rating is placed as a background image in the ul */ +/* use the background position according to the table above to display the required images*/ +.rating{ + width:80px; + height:16px; + margin:0px 0px 20px 0px; + padding:0; + list-style:none; + float: left; + position:relative; + background: url(../images/star-matrix.gif) no-repeat 0 0; +} +ul.rating li { + cursor: pointer; + /*ie5 mac doesn't like it if the list is floated\*/ + float:left; + /* end hide*/ + text-indent:-999em; +} +ul.rating li a { + position:absolute; + left:0; + top:0; + width:16px; + height:16px; + text-decoration:none; + z-index: 200; +} +ul.rating li.one a {left:0} +ul.rating li.two a {left:16px;} +ul.rating li.three a {left:32px;} +ul.rating li.four a {left:48px;} +ul.rating li.five a {left:64px;} +ul.rating li a:hover { + z-index:2; + width:80px; + height:16px; + overflow:hidden; + left:0; + background: url(../images/star-matrix.gif) no-repeat 0 0 +} +ul.rating li.one a:hover {background-position:0 -96px;} +ul.rating li.two a:hover {background-position:0 -112px;} +ul.rating li.three a:hover {background-position:0 -128px} +ul.rating li.four a:hover {background-position:0 -144px} +ul.rating li.five a:hover {background-position:0 -160px} + +/* this is used to remove the hover affect */ +/* use the background position according to the table above to display the required images*/ +.rated{ + width:80px; + height:16px; + margin:0px 0px 20px 0px; + padding:0; + list-style:none; + float: left; + position:relative; + background: url(../images/star-matrix.gif) no-repeat 0 0; +} +ul.rated li { + cursor: pointer; + /*ie5 mac doesn't like it if the list is floated\*/ + float:left; + /* end hide*/ + text-indent:-999em; +} +ul.rated li.one a {left:0} +ul.rated li.two a {left:16px;} +ul.rated li.three a {left:32px;} +ul.rated li.four a {left:48px;} +ul.rated li.five a {left:64px;} + +/* add these classes to the ul to effect the change to the correct number of stars */ +.nostar {background-position:0 0} +.onestar {background-position:0 -16px} +.twostar {background-position:0 -32px} +.threestar {background-position:0 -48px} +.fourstar {background-position:0 -64px} +.fivestar {background-position:0 -80px} +/* end rating code */ +h3{margin:0 0 2px 0;font-size:110%} \ No newline at end of file diff --git a/site/runshops/styles.css b/site/runshops/styles.css new file mode 100644 index 0000000..d2bd5d1 --- /dev/null +++ b/site/runshops/styles.css @@ -0,0 +1,545 @@ +/* Defaults */ +body { +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +color: #000; +padding: 0; +margin: 15px 0 5px; +} + +.indent { +margin-left: 10px; +} + +.indentlarge { +margin-left: 30px; +} + +.indenthuge { +margin-left: 50px; +} + +.rightside { +text-align: right; +} + +form { +margin: 0; +padding: 0; +} + +/* Links +a:link { +color: #000099; +text-decoration : none; +padding : 0; +margin : 0; +} +a:visited { +color: #000099; +text-decoration : none; +} +a:hover { +color: #000000; +text-decoration : underline; +} +a:active { +color: #999999; +text-decoration : none; +}*/ + +.modify{ +text-decoration: none; +color: #FFFFFF; +font-weight: bold; +font-size: 14pt; +} + +.modify:hover{ +text-decoration: underline; +} + +/* Outer layer */ + +#outer { +text-align: left; +background: #ffffff; +color: inherit; +/**border: 1px solid #000000;**/ +margin: 0 auto; +padding: 0; +} + +/* Header */ + +.header { +background: #fcce32; +} + +.logolink { +padding: 6px 3px; +} + + .logolink a { +background: inherit; +} + +.hdr-links { +width: 230px; +text-align: center; +font-size : 10px; +padding : 0; +margin: 0; +} + +a.hdrnav:link { +color: #000099; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +} +a.hdrnav:visited { +color: #000099; +background : inherit; +text-decoration : none; +} +a.hdrnav:hover { +color: #000; +background : #fff; +text-decoration : underline; +} +a.hdrnav:active { +color: #999; +background : inherit; +text-decoration : none; +} + +.hdr-links .access { +text-decoration : underline; +color : #000; +background : inherit; +} + + +/* Top Nav Bar */ + +.top-nav { +height: 25px; +background: #c5c6ac; +color: inherit; +border: solid #000000; +border-width: 1px 0; +} +.top-nav-inner { +padding: 5px 10px; +} +.top-nav-inner a:link { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: none; +} +.top-nav-inner a:visited { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: none; +} +.top-nav-inner a:hover { +background: inherit; +color: #555555; +font-weight: bold; +text-decoration: underline; +} +.top-nav-inner a:active { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: underline; +} + + + +/* Image Bar */ + +.image-bar { +clear:both; +} + +.title-img { +border: 1px solid #000; +color: #ffffff; +background: #343234; +padding: 0 0 0 10px; +} + +.section-title { +font-size: 16px; +font-weight: bold; +font-family: verdana, arial, tahoma, sans-serif; +} + +.graphic-img { +border: 1px solid #000000; +} + +.blank-img { +border: 1px solid #000000; +background: #fcce32; +} + +.blank-img-ads { +border: 1px solid #000000; +background: #fcce32; +} + + +/* Content Section */ + +/*.content { +padding: 0 4px 4px 4px; +margin: 0; +}*/ + +.left-col { +border: 1px solid #000000; +background: #fcce32; +width: 145px; +} + +.left-nav { +margin: 3px; +} + +.statenav { + margin: 4px; +} + +.lnav-hdr { +background: #c5c6ac; +color: #000000; +font-family: verdana, tahoma, arial, sans-serif; +font-size: 11px; +font-weight: bold; +border: 1px solid #000000; +margin-top: 16px; +padding: 2px 5px; +} + +.left-nav p { +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +line-height: 14px; +margin:5px; +} + +.left-nav .access { +text-decoration : underline; +color : #000000; +background : inherit; +} + +a.lhdr:link { +color: #000000; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +font-weight: bold; +} +a.lhdr:visited { +color: #000000; +background : inherit; +text-decoration : none; +font-size: 10px; +font-weight: bold; +} +a.lhdr:hover { +color: #000; +background : #fcfefc; +text-decoration : underline; +} +a.lhdr:active { +color: #555; +background : inherit; +text-decoration : none; +} + +input.nobutton { + background-color: #0A50A1; + font-weight: bold; + font-size: 9px; + color: white; +} + +input.nobutton:visited { + background-color: #fcce32; + font-weight: bold; + font-size: 9px; + color: white; +} + +input.nobutton:active { + background-color: #fcce32 + color: white; + font-weight: bold; + font-size: 9px; +} + +a.nav:link { +color: #000099; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +} +a.nav:visited { +color: #000099; +background : inherit; +text-decoration : none; +} +a.nav:hover { +color: #000; +background : #fcfefc; +text-decoration : underline; +} +a.nav:active { +color: #999; +background : inherit; +text-decoration : none; +} + + + +/* Main content box */ +.content-box { +padding: 0; +margin: 0; +} + +/* right content box */ +.rhcol { +float: right; +width: 160px; +margin: 0 0 10px 15px; +background: #fcfefc; +color: #000000; +} +.rhcont { +border-left: 1px solid #000000; +border-bottom:3px solid #000000; +} +.rnav-hdr { +background: #c5c6ac; +color: #000000; +font-family: verdana, tahoma, arial, sans-serif; +font-size: 12px; +font-weight:bold; +border-bottom: 1px solid #000; +border-left: 2px solid #000000; +margin: 3px 0; +padding: 2px; +} + +/* main content area */ + +.content-title { +margin: 5px 0px 15px; +font-size: 14px; +text-decoration: underline; +} + +.bottomindent { +margin-bottom: 5px; +} + +.content-title-blog { +margin: 5px 0px 2px; +margin-left: 8px; +font-size: 16px; +text-decoration: none; +} + +.content-title-no1 { +margin: 5px 0px 15px; +font-size: 14px; +text-decoration: none; +} + +.content-title-mod { +margin: 5px 10px 15px; +font-size: 14px; +text-decoration: underline; +} + +.content-title-no { +margin: 0px 0px 0px; +font-size: 12px; +text-decoration: none; +} + +.content-box .cont { +margin: 10px 10px 15px 10px; +font-size: 11px; +line-height: 15px; +} + +.content-box .cont-list { +margin: 2px 10px 5px 10px; +} + +.content-box .sub-hdr { +margin: 2px 10px 10px; +font-size: 12px; +text-decoration: underline; +} + +.content-box .sub-hdr-mod { +margin: 2px 10px 10px; +font-size: 11px; +text-decoration: none; +} + +/* Bottom footer box */ + +.footer-col { +margin: 0 0 0 4px; +} + +.footer-box { +border: 1px solid #000000; +background:#343234; +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +color:#ffffff; +padding: 0 0 0 15px; +} + +.footer-box .cont1 { +margin: 8px 10px 5px 5px; +font-size: 11px; +line-height: 15px; +} +.footer-box .cont2 { +margin: 2px 10px 15px 5px; +font-size: 11px; +line-height: 15px; +} + +.footer-box input { +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +} + +/* Copyright layer */ + +.copyright { +background: #c5c6ac; +font-family: verdana, arial, tahoma, sans-serif; +font-size: 10px; +font-weight: bold; +padding: 6px; +border: solid #000000; +border-width: 1px 0 0; +} + +.rh-ad { +padding: 10px 0; +} + +/* Terms of use on front page */ +.tou { +margin: 0 0 0 10px; +} + + +/* for templates info */ +.temp-lh { +float : left; +width : 100px; +margin : 0; +padding : 2px 10px 2px 0; +text-align : left; +} +.temp-rh { +margin : 0; +padding : 2px 0; +text-align : left; +} +.date { +font-size : 10px; +color : #787d63; +background : inherit; +} +.temp-list ul { +font-size : 11px; +margin : 0; +padding : 0; +list-style-type : none; +} +.temp-list { +padding : 0 0 0 15px; +} + +/* small*/ +small.fsize { +font-size : 9px; +color : #999999; +background : inherit; +} + +.code { +color : #663300; +background : #eeeeee; +font-family : verdana, arial, sans-serif; +font-size : 11px; +line-height : 20px; +padding : 4px 10px; +margin : 10px 20px; +border : 1px dashed #999999; +} + + +/* Search form on front page */ + +.search-form { +color: #000; +background: #c5c6ac; +padding: 5px; +margin: 0px 0 10px 30px; +border: 1px solid #333333; +width:465px; +text-align: center; +white-space: nowrap; +} + +/* Search forms on Search page */ + +.adv-search { +margin: 0 0 0 15px; +} + +/* Tool Tip */ +.tip { +cursor : help; +border-bottom : 1px dashed #555555; +} + +/* ADVERTISEMENTS */ +/* container div is 'id=adspace' */ +#adspace { +background: inherit; +} +.ads { +background: inherit; +margin: 5px 0 15px 4px; +font-size: 11px; +padding: 5px; +border: solid #555; +border-width: 1px 0 0 1px; +} +.ads a:link { font-size: 11px; } diff --git a/site/slowtwitch.com/www/Cool/index.html b/site/slowtwitch.com/www/Cool/index.html new file mode 100644 index 0000000..bf7358b --- /dev/null +++ b/site/slowtwitch.com/www/Cool/index.html @@ -0,0 +1,807 @@ + + + +<:: Welcome to Slowtwitch.com ::>: What's Cool + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + + +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > Cool
      +

      Cool Links (top 1%)

      +
      > >|
      +

      Bike Fit

      +
      +
      + +
      +
      +

      Bike positions of pro triathletes

      +
      +: + Ex pro duathlete, and Kuota sales guru, Paul Thomas apprised me of a very interesting experiment he undertook at the most recent Hawaiian Ironman. +"I sat on the Queen K Highway as the riders were ... +
      +
      +
      +

      Bike Fit > F.I.S.T. Tri bike fit system

      +
      +
      + +
      +
      +

      Armrest drop

      +
      +: + This article exists as part of a series of eleven on tri bike fit, and describes the proper armrest drop for a triathlete; how to measure it; how to determine it. +
      +
      +
      +

      +
      +
      + +
      +
      +

      Cockpit length

      +
      +: + This article describes how we find a rider's proper cockpit length. +
      +
      +
      +

      Interview

      +
      +
      + +
      +
      +

      An interview with US Navy Seal David Goggins

      +
      +: + For a guy who says he does not enjoy the physical toll of running, United States Navy SEAL David Goggins sure does a good job of fooling those of us watching from the sidelines. +
      +
      +
      +

      +
      +
      + +
      +
      +

      Craig Alexander talks to Slowtwitch

      +
      +: + Australian Craig Alexander had an amazing 2007 season and is now finally able to relax with his wife Neri and daughter Lucy. Craig and Neri were kind enough though to give Slowtwitch a few minutes. +
      +
      +
      +

      News

      +
      +
      + +
      +
      +

      2009 top 15 Kona finisher bikes - men

      +
      +: + We looked closer at the bikes of the top 15 finishers at the 2009 Ford Ironman World Championships in Hawaii. Brands and details of frames, wheels, aero bars, saddles and more. +
      +
      +
      +

      Products

      +
      +
      + +
      +
      +

      3T takes the shorty bar forward

      +
      +: + The so-called shorty bar remains, in triathlon, the largest market served by no mature, ready-to-ride, product. 3T moves the ball much further forward with its new Zefiro. +
      +
      +
      +

      +
      +
      + +
      +
      +

      Cannondale's Slice for 2008

      +
      +: + The Slice is not your father's Cannondale. Nor even your older brother's. It's all new and (gasp!) it's all carbon. +
      +
      +
      +

      +
      +
      + +
      +
      +

      Chamois cream: the best thing you're not using

      +
      +: + Friction is your friend, if you're going around a tight corner on your bicycle and staying upright is your aim. But down there in your cycling shorts, friction is your enemy. What you need is a ... +
      +
      +
      +

      +
      +
      + +
      +
      +

      Chrissie Wellington's Cervelo P2C

      +
      +: + When Chrissie Wellington recently stormed to a second place overall finish at the Alpe d'Huez triathlon she put her competitors on notice that the Ironman Hawaii win wasn't a fluke. For our "the ... +
      +
      +
      +

      Products > Old reviews > Old bike reviews

      +
      +
      + +
      +
      +

      2009 tri bikes at the entry level

      +
      +: + The flip side of our red hot demographic is that "entry level" in triathlon does not start at the entry level. Here's our $1500-and-up look at tri bikes for 09, starting with Cervelo, Felt and QR. +
      +
      +
      +

      Products > Running Footwear by brand

      +
      +
      + +
      +
      +

      2012 Kona top 15 men - run

      +
      +: + Here is a look at the top 15 overall men on the run at the 2012 Ironman World Championships with their respective run splits and details of their running shoes. +
      +
      +
      +

      Products > Things that Roll

      +
      +
      + +
      +
      +

      Bontrager TLR 2013 Review

      +
      +: + We review an all-new lineup of wheels, tires, and tire sealant from Bontrager. We’re impressed by their remarkably simple design and reasonably priced options… and the possibility for some ... +
      +
      +
      +

      Products > Tri Bike by price > Tri bikes by price for 2010

      +
      +
      + +
      +
      +

      2010 Tri bikes

      +
      +: + You might think it's still 2009, but it isn't, at least, not according to bike manufacturers. It's 2010 already for many of them, and here is what you can expect to see. +
      +
      +
      +

      Products > Tri Bike by price > Tri bikes by price for 2010 > Tri Bikes $2600-$3500 (2010)

      +
      +
      + +
      +
      +

      Cervelo P2 (2010)

      +
      +: + Over the past decade Cervelo simplified cable routing, added a chain stay adjustment screw, then came the move to carbon. Since then it's been pretty clear sailing. +
      +
      +
      +

      Products > Tri Bike by price > Tri bikes by price for 2011 > Tri Bikes $2900-$3700 (2011)

      +
      +
      + +
      +
      +

      Cannondale Slice 3 (2011)

      +
      +: + The industry has chosen pitched battle at a price range between $3300 and $3700. The Slice 3 sits right in the middle, and it holds its own admirably. Plasmas, Transitions, Speed Concepts, this is ... +
      +
      +
      +

      +
      +
      + +
      +
      +

      Cervelo P3 Ultegra (2011)

      +
      +: + Cervelo is to tri what Colnago is to road: a storied headbadge with a tradition and a caché unmatched by any bike brand. But the cycling world is catching up, or has caught up. How does the P3 ... +
      +
      +
      +

      Tech

      +
      +
      + +
      +
      +

      All About Tubeless

      +
      +: + While common on mountain bikes, many triathletes are unaware of the particulars of tubeless tire technology. We offer a complete overview, along with the key take-homes for the average road-based ... +
      +
      +
      +

      +
      +
      + +
      +
      +

      Armstrong on his Tri Bike

      +
      +: + Things are changing for Lance. Like, his position. What goes around comes around, and he's laregely come back 'round to his pre-bike-race tri position, with an assist from Slowtwitch forum members. +
      +
      +
      +

      +
      +
      + +
      +
      +

      Bottom Bracket How-To

      +
      +: + We cover the installation of a modern threaded, outboard bearing bottom bracket. Even if you never intend to replace one yourself, you may find this inside look to be enlightening. +
      +
      +
      +

      +
      +
      + +
      +
      +

      Bottom Bracket Standards

      +
      +: + The irony is that the newest, most relevant bottom bracket standards, such as BB90 and BB30, aren’t about bottom brackets. They’re about frame construction, and frame stiffness. +
      +
      +
      +

      +
      +
      + +
      +
      +

      Cassette How-To - Part 2

      +
      +: + We continue our How-To series on cassettes. This segment covers the nitty-gritty details of cassette spacers, freehub standards, and what to do in the event of a cassette removal problem. +
      +
      +
      +

      +
      +
      + +
      +
      +

      Crank arm lengths for tri

      +
      +: + The question is more common among road riders than triathletes, but is probably more impactful on the latter group: what crank length should you ride? The answer is counterintuitive; probably the ... +
      +
      +
      +

      Training > Beginners

      +
      +
      + +
      +
      +

      Beginner Training Schedule

      +
      +: + It's been almost 20 years since this schedule debuted, aimed at the rank beginner wanting to compete in a triathlon. With a spruce-up, here it is back on the Slowtwitch servers. +
      +
      +
      +

      +
      +
      + +
      +
      +

      Beginners: Week 1

      +
      +: + As we used to say in the 70s, "This is the first day of the rest of your life." Have a peace sign tank top and a Keep on Truckin' bumper sticker? Great, you're ready to start your first day of tri ... +
      +
      +
      +
      > >|
      +
      +
      +
      +
      + +
      +
      + +
      + + diff --git a/site/slowtwitch.com/www/Cool/more2.html b/site/slowtwitch.com/www/Cool/more2.html new file mode 100644 index 0000000..6701b3f --- /dev/null +++ b/site/slowtwitch.com/www/Cool/more2.html @@ -0,0 +1,806 @@ + + + +<:: Welcome to Slowtwitch.com ::>: What's Cool + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + + +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > Cool
      +

      Cool Links (top 1%)

      +
      |< < > >|
      +

      Bike Fit > Choosing a Tri Bike via Stack and Reach

      +
      +
      + +
      +
      +

      Long and low, short and narrow

      +
      +: + It's late November. Don't you love the change of seasons? Depending where you are, the greens are turning to yellow, and the yellows red. But that is not the change to which I refer. +
      +
      +
      +

      Bike Fit > F.I.S.T. Workshops

      +
      +
      + +
      +
      +

      F.I.S.T. BASIC Bike Fit Workshops

      +
      +: + Our first workshop of the 2018 F.I.S.T. Bike Fit Workshop season will be held in November, 2018. +
      +
      +
      +

      Bike Fit > General Fit Articles

      +
      +
      + +
      +
      +

      Finding the right bike from the bars back

      +
      +: + There are some very sexy integrated aero bars on the market right now. Profile Design's Carbon X. 3T's Ventus. Visiontech's Trimax. All these feature integrated stems. How does one divine what ... +
      +
      +
      +

      Bike Fit > Road Bike Fit

      +
      +
      + +
      +
      +

      Optimizing all Road Riding Positions

      +
      +: + Seated, hands-on-hoods is one of five. If you've been fitted only to this position, you bought one-fifth of a proper fit session. All five postures inform both bike and handlebar geometries. +
      +
      +
      +

      Features

      +
      +
      + +
      +
      +

      Ironman Hawaii 2008 - the morning swim.

      +
      +: + There are still 4 days to go until the 2008 Ironman Hawaii, but the venues are already busy as if the race where tomorrow. We went down to the swim start this morning and checked out the action ... +
      +
      +
      +

      +
      +
      + +
      +
      +

      More from le Tour 2008

      +
      +: + Part four of Jay Prasuhn's latest and greatest from le Tour. New and custom shades from Oakley, Bell's new lid, FSA goes asymmetric, white is the new black, and more... +
      +
      +
      +

      Opinion

      +
      +
      + +
      +
      +

      Kona Odds - The women

      +
      +: + Last year, Chrissie Wellington was an unkown dark horse and her shocking victory was the biggest upset in Kona history. hius year, it will be the biggest upset in Kona history if she doesn't win. +
      +
      +
      +

      Products

      +
      +
      + +
      +
      +

      Ironman Hawaii 2008 - the top 15 men and their bikes

      +
      +: + What kind of bikes were the top 15 pro men riding at the Ford Ironman World Championships in Kona, Hawaii? Slowtwitch took a few images of the men and their machines. +
      +
      +
      +

      +
      +
      + +
      +
      +

      Ironman Hawaii 2008 - the top 15 women and their bikes

      +
      +: + Although the Ford Ironman World Championships and any other triathlon isn't only about the bike, we still want to share what kind of bikes the top 15 pro women rode in Kona. +
      +
      +
      +

      +
      +
      + +
      +
      +

      Mid-priced tri bikes

      +
      +: + For those who're looking toward a bike they'll keep for a long stretch, but won't have to make payments over a long stretch, here's our first round look at your price category: $2500 to $3000 +
      +
      +
      +

      Products > Handlebars

      +
      +
      + +
      +
      +

      Low Profile top caps

      +
      +: + Tall headset top caps are my great while whales. I don't want to eliminate them, just cut them down to size. John Cobb to the rescue. +
      +
      +
      +

      Products > Old reviews > Old bike reviews

      +
      +
      + +
      +
      +

      Entry-level tri bikes: Trek and Fuji

      +
      +: + There aren't many complete tri bikes for sale under $1900 anymore. But there are six, three of which we've already reviewed, two more are reviewed here, the Trek Equinox 7 and the Fuji Aloha 2.0 +
      +
      +
      +

      Products > Old reviews > Old shoe reviews

      +
      +
      + +
      +
      +

      Lightweight Running: Spring 09

      +
      +: + This is the first of several articles looking at lightweight technical running footwear, and eventually all important models in all categories of running footwear, offered for sale for the Spring 09 ... +
      +
      +
      +

      +
      +
      + +
      +
      +

      More lightweight trainers 09

      +
      +: + Our look at lightweight trainers for Spring 09 continues. In Part I we looked at shoes from Nike, Saucony and Asics. Now we move to Adidas, New Balance and Mizuno. +
      +
      +
      +

      Products > Saddles

      +
      +
      + +
      +
      +

      ISM Long Term Test

      +
      +: + We've had our hands on several models of ISM saddles for well over a year. We share our thoughts, experiences, and recommendations on these ever-popular saddles. Are they right for you? Read on. +
      +
      +
      +

      Products > Things that Roll > Tires

      +
      +
      + +
      +
      +

      Determining Proper Tire Pressure

      +
      +: + Our Tech Editor continues our series on tire pressure, getting into the details of how to dial in your optimal setting. +
      +
      +
      +

      +
      +
      + +
      +
      +

      Fast Tires for 2012 - 1

      +
      +: + Part-1 of a two-part overview of current tire offerings, clincher and tubular, for all major brands. Consider this an "as-of-early-2012" summary of what's out there. We focus primarily on tires sold ... +
      +
      +
      +

      Products > Tri Bike by brand

      +
      +
      + +
      +
      +

      Kona 2011 - Top 15 men on the bike

      +
      +: + Here is a closer look at the top 15 men at the Ironman World Championships aboard their bikes. Want to know what they were using in terms of frames and components? Look no further. +
      +
      +
      +

      +
      +
      + +
      +
      +

      Kona top 15 men on bikes

      +
      +: + Our feature of the top 15 overall males at the 2012 Ironman World Champs on their bikes with details of material used and a look at their actual riding position. +
      +
      +
      +

      Tech

      +
      +
      + +
      +
      +

      Front Derailleur How-To

      +
      +: + This is your How-To guide for setting up front derailleurs. We cover the basics for cable-driven systems, including a troubleshooting section for odd problems. +
      +
      +
      +

      +
      +
      + +
      +
      +

      Implications of Rim Width

      +
      +: + We explore the topic of rim width and tire width, and what it means to you. Should you go wide? Should you go narrow? What are the implications? We discuss these topics, and offer a complete listing ... +
      +
      +
      +

      Training > Cycling

      +
      +
      + +
      +
      +

      Descending

      +
      +: + This popular tutorial on descending was first written in 2000 and has been reworked and updated for today. +
      +
      +
      +

      Training > Running

      +
      +
      + +
      +
      +

      How to Kill—Your 10k PR

      +
      +: + I believe just about every reasonably fit male the age of 45 has the theoretical physiological capacity to run his age for a 10k, that is, a 10k in 45 minutes. +
      +
      +
      +

      Training > Swimming

      +
      +
      + +
      +
      +

      How to Position your Head while Swimming

      +
      +: + By lifting the head, our hips automatically sink down. The body takes on a slight incline from head to toe, perhaps 7 to 10 degrees, as the swimmer labors through the water. +
      +
      +
      +

      +
      +
      + +
      +
      +

      How To Pull Underwater

      +
      +: + There are huge differences in how you can pull underwater with your arm; differences in power and differences in frontal drag. But, like so many aspects of swimming technique, the underwater pull ... +
      +
      +
      +
      |< < > >|
      +
      +
      +
      +
      + +
      +
      + +
      + + diff --git a/site/slowtwitch.com/www/Cool/more3.html b/site/slowtwitch.com/www/Cool/more3.html new file mode 100644 index 0000000..ae56d2d --- /dev/null +++ b/site/slowtwitch.com/www/Cool/more3.html @@ -0,0 +1,806 @@ + + + +<:: Welcome to Slowtwitch.com ::>: What's Cool + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + + +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > Cool
      +

      Cool Links (top 1%)

      +
      |< < > >|
      +

      Bike Fit > Choosing a Tri Bike via Stack and Reach

      +
      +
      + +
      +
      +

      Slowtwitch debuts Geometry Calculator

      +
      +: + The F.I.S.T. tri bike geometry calculator is open for beta testing. Your fitter plunks numbers into our online engine and, poof, your custom bike geometry! +
      +
      +
      +

      +
      +
      + +
      +
      +

      Stack & Reach Primer: Chapter One

      +
      +: + Earlier in the decade I wrote an article on how bikes are sized, and the gist was that we ought to be looking at bikes from the point of view of head tubes and top tubes, and that's it. -- no more ... +
      +
      +
      +

      +
      +
      + +
      +
      +

      Stack & Reach Primer: Chapter Three

      +
      +: + In the final chapter of our stack and reach primer, we compare stack and reach with other ways of remaking sizing nomenclature. +
      +
      +
      +

      +
      +
      + +
      +
      +

      Stack & Reach Primer: Chapter Two

      +
      +: + Our primer on stack & reach continues with the second chapter in the series. We examine how stack and reach enable you to understand how a given bike will, or will not, fit you. +
      +
      +
      +

      Bike Fit > F.I.S.T. Tri bike fit system

      +
      +
      + +
      +
      +

      Seat height

      +
      +: + This is article number five out of eleven describing our F.I.S.T. Tri bike fit process. +
      +
      +
      +

      +
      +
      + +
      +
      +

      The F.I.S.T. Method for fitting triathletes to their bikes

      +
      +: + Slowtwitch means different things to different athletes, but tri bike fit is the thing we're probably best known for. Herein is a description of our process for tri bike fit. +
      +
      +
      +

      Bike Fit > General Fit Articles

      +
      +
      + +
      +
      +

      Positional problems

      +
      +: + “Why am I uncomfortable on my bike?” Do you have occasion to ask this? We endeavor to find the answer in our series on the subject. Our first installment focused on contact points. This is ... +
      +
      +
      +

      Bike Fit > Road Bike Fit

      +
      +
      + +
      +
      +

      Reasonable bike fit expectations

      +
      +: + Slowtwitch forum members too often (justifiably) complain about bad results from bike fit sessions. As of 2018 here is what you should expect. +
      +
      +
      +

      +
      +
      + +
      +
      +

      Road Bike Fit

      +
      +: + This road bike fit protocol is designed to work with fit bikes that adjust horizontally and vertically; to take advantage of stack and reach data matching; and to consider all the positions common to ... +
      +
      +
      +

      News

      +
      +
      + +
      +
      +

      The bikes of the top 15 men in Kona 2010

      +
      +: + A popular feature each year is a look at the top 15 Pros in Kona on their bikes, and here we go again. This is your chance to check out the gear of these athletes and the positions they hold while ... +
      +
      +
      +

      +
      +
      + +
      +
      +

      Top Ironman Hawaii Finishers Archive

      +
      +: + With the 2022 IRONMAN World Championships almost upon us we look back at the Top Ironman Hawaii Finishers Archive with the top overall finishers dating all the way back to 1978. +
      +
      +
      +

      Products

      +
      +
      + +
      +
      +

      The bikes of the top ten pro women in Kona

      +
      +: + We have uploaded a gallery of images of the top 10 Pro women aboard their bikes. +
      +
      +
      +

      +
      +
      + +
      +
      +

      The running shoes of the top 10 men in Kona

      +
      +: + While we started to look closer at the bikes of the top finishers at Ironman Hawaii last year already, this year we also wanted to know more about their running shoes. +
      +
      +
      +

      +
      +
      + +
      +
      +

      The running shoes of the top 10 women in Kona

      +
      +: + Chrissie Wellington and Sandra Wallenhorst ran sub 3 hours at the 2008 Ford Ironman World Championships and finished 1st and 3rd respectively. We looked closer at the shoes which carried them and ... +
      +
      +
      +

      +
      +
      + +
      +
      +

      The top ten pro men in Kona and their bikes

      +
      +: + Here is a closer look at the bikes on which the top 10 male Pros competed in Kona. +
      +
      +
      +

      Products > Old reviews > Old shoe reviews

      +
      +
      + +
      +
      +

      Running footwear 09: racing flats

      +
      +: + If you're a 180 pound triathlete, a 6 ounce shoe is probably out. But if you're a 145 pounder with a good footfall, an 8 ounce shoe might be in. Here's our first round of overviews of 09's racing ... +
      +
      +
      +

      Products > Running Footwear by brand

      +
      +
      + +
      +
      +

      Tech footwear demystifier

      +
      +: + We write about running footwear here a lot. It seemed appropriate to tell you how to read and interpret these articles. What is a "pronator?" What is a "last?" What is an "outsole," and a "medial ... +
      +
      +
      +

      Products > Saddles

      +
      +
      + +
      +
      +

      Saddle Theory: Part II

      +
      +: + Are there differences between how you sit, and what you do, aboard a saddle on a road bike versus a tri bike? Yes. Two differences, and they're big. What makes this possible? Handlebar design. +
      +
      +
      +

      +
      +
      + +
      +
      +

      Saddle Theory: Part III

      +
      +: + Split nose saddles: I separate these into two categories, those that appear like standard saddles, creating a fjord out of what in other saddles is a lake; versus wide split-nosed saddles like the ... +
      +
      +
      +

      Products > Things that Roll

      +
      +
      + +
      +
      +

      Shimano Wheel Review 2012 and 2013

      +
      +: + We take an in-depth look at the wheel offerings from Shimano for 2012, along with a preview of soon-to-be-released 2013 product. Has the Japanese giant finally gone aero? +
      +
      +
      +

      Products > Things that Roll > Race Wheels

      +
      +
      + +
      +
      +

      Shimano 9000-series wheels

      +
      +: + We take a close look at the full line of 2013 Shimano Dura Ace 9000-series carbon wheels. They range from 24mm to 75mm deep, and are available in clincher or tubular. +
      +
      +
      +

      Products > Tri Bike by price > Tri bikes by price for 2011 > Tri Bikes $2900-$3700 (2011)

      +
      +
      + +
      +
      +

      Scott Plasma 20 (2011)

      +
      +: + The Plasma 2 frame was unobtainable during 2010 for a price below $5000. For 2011, this high quality frameset can be had in a bike selling complete, with a very nice gruppo, for $3300. Today we ... +
      +
      +
      +

      +
      +
      + +
      +
      +

      Specialized Transition Pro (2011)

      +
      +: + This bike is further evidence that the most hotly contested price category in tri bike land is the $3300 to $3700 range. The big boys—Trek, Cervelo, Felt, Cannondale, Scott—have gone all-in here. ... +
      +
      +
      +

      Tech

      +
      +
      + +
      +
      +

      The Physics of Moving a Bike

      +
      +: + How power propels a bike: This is the second of two introductory articles preparing for a year-long series on training with power. +
      +
      +
      +

      Training > Swimming

      +
      +
      + +
      +
      +

      Stalling in the water

      +
      +: + The bane of the untutored swimmer is stalling in the water. Does this describe you? Here are two tests to unearth this problem, and the solutions if you're among the afflicted. +
      +
      +
      +
      |< < > >|
      +
      +
      +
      +
      + +
      +
      + +
      + + diff --git a/site/slowtwitch.com/www/Cool/more4.html b/site/slowtwitch.com/www/Cool/more4.html new file mode 100644 index 0000000..85c036a --- /dev/null +++ b/site/slowtwitch.com/www/Cool/more4.html @@ -0,0 +1,611 @@ + + + +<:: Welcome to Slowtwitch.com ::>: What's Cool + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + + +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > Cool
      +

      Cool Links (top 1%)

      +
      |< <
      +

      Bike Fit

      +
      +
      + +
      +
      +

      Tour de Bike Fit

      +
      +: + Read this first, if "tri bike fit" is new to you. It's a guide to the dozens of articles in the bike fit section. The articles are grouped, and we offer our advice about the order in which they ... +
      +
      +
      +

      Bike Fit > General Fit Articles

      +
      +
      + +
      +
      +

      Tri, road, or tweener?

      +
      +: + There are three ways to ride a bike: road race (the way pros do it); triathlon (the way pros do it); and that strange morphing of the two which we call the "tweener." +
      +
      +
      +

      Products

      +
      +
      + +
      +
      +

      Torbjorn Sindballe's Argon 18 E-114

      +
      +: + As part of a new series called "the bikes of the pros" we will be looking a bit closer at the race machines of several pro athletes. We start the series here with the bike of Torbjorn Sindballe. +
      +
      +
      +

      +
      +
      + +
      +
      +

      TriSuit Shootout Part 3: 2XU

      +
      +: + In two shorts years, 2XU has moved from newbie to triathlon goliath. We take a look at their trisuits, including the new for 2008 Super Elite Endurance suit. +
      +
      +
      +

      Products > Things that Roll

      +
      +
      + +
      +
      +

      Tubeless Sealant Tips and Strategy

      +
      +: + We dive into tips and tricks specific to tubeless tire sealant. How do you use it or remove it? How often must you add it? What are the best brands? +
      +
      +
      +

      +
      +
      + +
      +
      +

      Video: How to Properly Clean Out Tubeless Tires

      +
      +: + Our Tech Editor takes a dive into the best methods for cleaning out old tubeless tire sealant – to keep your tires fresh and functioning. +
      +
      +
      +

      +
      +
      + +
      +
      +

      Zipp 101 Long Term Test - Stealth Aero?

      +
      +: + Zipp's 101 wheelset looks like a training wheelset, but they perform like they were meant for racing. So, which are these all-aluminum but still toroidal wheels? It seems the best answer is both. +
      +
      +
      +

      Products > Tri Bike by price > Tri bikes by price for 2011 > Tri Bikes $2900-$3700 (2011)

      +
      +
      + +
      +
      +

      Trek Speed Concept 7.5 (2011)

      +
      +: + Trek has finally come out to play in the sport of triathlon. They engage in every price range with a bike that's imaginative and well conceived. The Speed Concept 7.5 is a strong entry in the mid-$3 ... +
      +
      +
      +

      Training > Cycling

      +
      +
      + +
      +
      +

      Want to speed up? Slow down

      +
      +: + Want hidden speed? Speed that costs you nothing, or very little? Adroitly managing your speed around two corners can save you more time than your aero helmet will gain you over the whole race. +
      +
      +
      +

      Training > Swimming

      +
      +
      + +
      +
      +

      Your Best Freestyle Technique

      +
      +: + Gary Hall, Sr., demonstrates, and explains, hip- versus shoulder-driven freestyle. Which technique is best for you? One, the other, or something in between? +
      +
      +
      +
      |< <
      +
      +
      +
      +
      + +
      +
      + +
      + + diff --git a/site/slowtwitch.com/www/Indoor_Training/A_Newbie_Tackles_TrainerRoad_6472.html b/site/slowtwitch.com/www/Indoor_Training/A_Newbie_Tackles_TrainerRoad_6472.html new file mode 100644 index 0000000..eebefa4 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/A_Newbie_Tackles_TrainerRoad_6472.html @@ -0,0 +1,604 @@ + + + + +A Newbie Tackles TrainerRoad - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      A Newbie Tackles TrainerRoad

      + +
      +
      +
      +

      Clamping my bike up to a CycleOps Fluid 2 Trainer, pedaling while dying of boredom waiting for the stopwatch to hit 90 minutes, was the height and breadth of my indoor riding experience. This was a problem because I do most of my bike training on a stationary trainer. I ride outside on Sundays. Every other workout – 3 or 4 per week – is performed indoors, on the trainer.

      I didn’t have a clue what my power was or even what FTP meant. I was doing intervals and shifting gears but I had no idea how hard I was working or if I was getting the most out of my workouts. My bike splits during the 2016 season reflected this.

      When I was gently admonished to consider a power-based cycling training system I thought, “I have nothing to lose, my cycling couldn’t get any worse.” I had no idea what a smart trainer was or that bike apps existed. Yes, I’m a Slowtwitch reader and no, I never clicked on the thread Trainer road.com (which now has over 3,500 responses).

      +

      I race in the women’s 30-34 age group. I raced my first 70.3 last year at Eagleman and I finished in what I thought was a fairly respectable 5:30:32. The bike leg took me 3 hours and 7 minutes which was the weakest part of my race. Since I’m still pretty new to cycling I figured why not give TrainerRoad a try. I’d be the perfect test dummy; an untainted juror.

      My first step was making sure I had the right equipment. I upgraded to a CycleOps Hammer and that change alone has blown me away. Above are the changes made in my woman cave.

      The second step involved downloading TrainerRoad, and I am NOT tech savvy. Still, it took me took fewer than 3 minutes.

      +

      Next up was pairing my device using Bluetooth. Lucky for me the set up process was seamless. If something takes me more than five minutes to figure out I’m pulling up a YouTube video. If that doesn’t work, I’m returning the product.

      Now for the hard part, the Power Test. There are two options: an 8-minute test, and a 20-minute test. Both determine your functional threshold power (FTP), which as you all know is a good approximation for the maximum power you can sustain for one hour. I was advised to do the longer test. I made sure I was well rested so that I could get an optimal reading. Result: 137. Above is my 20-minute FTP Test Results from April 6, 2017.

      Yes, that’s right 137! Irrespective of that number I am not an especially weak athlete! I had just run a standalone 3:00:06 marathon a few weeks before the test. So, that FTP result shocked me. I felt that I was very fit; I just didn’t know how to translate that fitness to the bike.

      +

      With 8 weeks to prepare for Ironman 70.3 Eagleman – and my hope to qualify for 70.3 Worlds later in the year – it was time to get to work. TrainerRoad gives you a few options: choose your own workouts (over 1,500 options), create your own workout (by using Workout Creator), or follow a training plan (structured training from a USAC Level I coach). Since I already have a coach I let her select the workouts.

      A few things I noticed right away: First, TrainerRoad allows you to automatically sync with TrainingPeaks after each workout; second, you have the option of choosing workouts that provide continuous live instruction and suggestions (cadence, position, etc) via text. Third, the app beeps when there are three seconds left in an interval.

      And the best part: I don’t have to drive to find hills (living in a pancake flat area is a blessing and a curse). Workouts like z-3LC Climbing and Blackcap simulate a hilly ride.

      +

      On June 11th (8 weeks later) I was finally able to test my fitness gains. On June 12, 2016, I covered the 56-mile course in 3:07:37 (averaging 17.91 mph). On June 11, 2017, I covered the same course in 2:50:18 (averaging 19.73 mph). Huge improvement on the prior year. Obviously there are other factors to consider: I’m somewhat fitter than I was a year ago, I’m in a better position on my bike, and I’ve raced on the course before.

      Still, riding my workouts now with TrainerRoad staring back at me, along with the upgrade to a smart trainer, were the obvious training differences as I consider what was most responsible for a 17-minute improvement year-over-year. It’s easy to think you’re working hard when you’re sweating. I actually thought I was pushing myself until I put my trainer in erg mode and was forced to stay within targeted zones based on my FTP. Do that week after week and you’ll start to see a difference! (Well, I can’t speak for anyone else, but I saw a big difference.)

      Other elements worth a mention: No more watching the clock! No more timing my own intervals. No need to shift gears since the resistance changes are automatic. It’s more enjoyable with the Hammer and TrainerRoad because I’m more engaged. I’m constantly watching my target power to make sure I’m staying in range; I’m reading the instructions and suggestions and following along, and the beep alerts give me incentive to keep pushing through the end of intervals.

      My only criticism, or maybe it’s just a feature recommendation, would be an option to make all prompts audio, that is to say, spoken.

      +

      Here I am, with my dad, at the 70.3 Eagleman race. I have no relationship to TrainerRoad, but I must disclose that I did get the tee shirt free! I am training now for the 2017 Ironman 70.3 World Championships in Chattanooga, and that 17-minute drop off my bike split is the biggest reason I did qualify for that race.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/A_Quick_Peek_at_Road_Grand_Tours_7181.html b/site/slowtwitch.com/www/Indoor_Training/A_Quick_Peek_at_Road_Grand_Tours_7181.html new file mode 100644 index 0000000..8a5e742 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/A_Quick_Peek_at_Road_Grand_Tours_7181.html @@ -0,0 +1,654 @@ + + + + +A Quick Peek at Road Grand Tours - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      A Quick Peek at Road Grand Tours

      + +
      +
      +
      +

      In the indoor cycling world, we’re getting a lot of really great choices to “entertrain” us. We’ve been exploring some of those options here, including Zwift and Rouvy.

      +

      Today we’re going to take an initial look at a product that has been in Beta and is going live soon - Road Grand Tours. Let’s get a glimpse of what Road Grand Tours looks like with a brief video:

      +

      +

      As you can see in the video, Road Grand Tours is an online multiplayer cycling simulator. For those of you familiar with Zwift, you’ll note some similarities - you are modeled as an avatar riding through a virtual world. The Road Grand Tours team has put quite a bit of emphasis on making a very good looking virtual world. I have been lucky to be part of the beta for over a year already, and have seen RGT continue to improve and provide a solid user experience.

      Since it is still in beta, we won’t go into a lot of detail in this article about system requirements and the installation process. We’ll focus on highlighting some of the features of RGT.

      +

      Location, Location, Location

      One of the most fun features incorporated into today’s indoor training software is the ability to ride in places all around the world. Road Grand Tours provides several routes modeled after real locales such as Cap Formentor in Mallorca, Spain or Canary Wharf in London.

      For each of the routes, you have the ability to choose several starting points - you don’t always have to start in the same place, and you can also start where you left off last time. If you see a friend out in the virtual world, you can choose to catch up and ride with them, too. Once you’ve decided on a starting point, you just click and go.

      +

      When I look at indoor training software, one of the primary needs I want to see addressed is performance information - measurement of my efforts, any biometric feedback my sensors are gathering, and how that translates to my performance on screen. The Road Grand Tours screen layout checks all of those boxes by including your biometric statistics, map, elevation information, and more in a relatively clean dashboard-type format. You have the ability to turn the various visual elements on or off to show as much or as little as you care to see.

      There are a few basic shortcut keys to make it easy for you to interact with the Road Grand Tours environment, as well as with other riders. As you ride through places such as Belgium or Italy, you can enjoy the scenery from multiple perspectives afforded by 6 different camera angles.

      Overall, the controls and interface are clean and intuitive, and you don’t have to be a tech whiz to get in and enjoy a ride in Road Grand Tours.

      +

      Choose Your Weapon

      As we all know, cycling is a fashion industry as much as it is a sport. Road Grand Tours has you and your avatar covered when you want to ride in style. You can select one of several road bikes from BMC, Ridley, or 8bar.

      Once you’ve picked your trusty steed, you can choose from a variety of kits from Adidas and others, helmets from Lazer, and eyewear from Adidas.

      +

      Be Part of The Community

      As an online multiplayer game, Road Grand Tours offers the ability to feel like part of something bigger than just you on your bike, sweating it out in your pain cave. These tools are great for providing motivation in several different ways. While riding in Road Grand Tours, you’ll see other people from around the world sharing the road, have the opportunity to participate in events, and also see how you stack up to the competion with built-in Strava segments.

      +

      Are You Ready to join the Grand Tours?

      If you like what you see, you can still try to sign up for the beta. In order to run RGT, you will need to be using a PC or Mac with a 64-bit operating system. Your Road Grand Tours experience will depend on having some good hardware:

      Minimum System Requirements (low quality setting)
      2 GHz CPU
      4 GB RAM
      Intel HD integrated graphics

      Recommended System Requirements (high quality settings)
      3GHz CPU
      4GB RAM
      nVidia GTX 970 or equivalent card

      You will also need a smart trainer that supports ANT+ communications. Bluetooth, as well as the ability to calculate virtual power using a speed sensor with a regular, non-smart trainer, are coming soon.

      Overall, Road Grand Tours is off to a great start and if you have the right hardware, certainly worth a look. It’s hard not to look at Road Grand Tours without comparing it to similar products such as Zwift. Zwift has quickly moved to the forefront in this space, with a massive rider presence at any given time of day, continued improvement, a great feature set, and a community being built around it. How does anybody else catch up to that?

      Road Grand Tours has great graphics, an intuitive and clean interface, and a good start on providing a variety of locales to explore. Events are available, but there is a feeling that you are all alone out there. It lacks workouts or training elements that get back to the primary purpose of indoor training. It also is a bit more graphic intensive, and that’s going to put more emphasis on having a relatively powerful gaming PC or Mac to enhance the experience. Is Road Grand Tours right for you? Right now, it’s free to join the Beta, so give it a try and help shape the future of Road Grand Tours.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Attacking_Alpe_du_Zwift_7691.html b/site/slowtwitch.com/www/Indoor_Training/Attacking_Alpe_du_Zwift_7691.html new file mode 100644 index 0000000..1467904 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Attacking_Alpe_du_Zwift_7691.html @@ -0,0 +1,729 @@ + + + + +Attacking Alpe du Zwift - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Attacking Alpe du Zwift

      + +
      +
      +
      +

      It didn’t take more than a handful of rides on Zwift before I started wondering how quickly I could level up to unlock the gates and tackle Alpe du Zwift. Modeled after France’s legendary mountain, Alpe d’Huez, the 7.6-mile climb averaging 8.5% features 21 switchback turns and gains 3,396 feet before reaching the top.

      Luckily for me and the thousands of others who rushed to the virtual gaming platform during the shelter-in-place orders in March, Zwift HQ decided to take down the barriers and provide anyone level 6 and above access to the grueling climb.

      When I think back to the first time I tackled the climb on March 31st, I guess it was a bit of a miracle I even made it to the top. I hadn’t yet learned it was possible to adjust the trainer’s level of difficulty setting and wrestled with the bike one agonizing pedal stroke at a time at 100% intensity. I even saw sparks fly off my cassette a few times from pedaling so slowly and creating so much friction. I slumped over my handlebars upon reaching the top having sufficiently self-combusted as I hadn’t yet purchased a high powered fan. But despite being too exhausted and overheated to even soft pedal the descent that day, I had taken the bait and the climb had me hooked.

      +

      + +

      + +

      Over the past two months I’ve completed the climb 15 times and have learned a great deal from other riders, those with far more experience and familiarity with best practices on Zwift as well as those who have mastered pacing up the monster mountain. I even managed to convince the most voracious Zwifter I know, reigning Kona Age Group World Champ Jana Richtrova, a Level 50 Zwifter, to face off in a head to head handicapped race to see if that extra motivation would propel us to new PR’s up the steep slopes.

      Below are some of the tips I learned and applied off the advice of others as well as some of Jana’s words of wisdom for anyone looking to maximize your efforts on Alpe du Zwift.

      +

      + +

      +

      At the time of our face-off, Jana had completed the climb 13 times, and 7 times since March 19th. Her times were getting faster each week with a best of 45:21 on May 17th. The fastest I had made it up was 46:58 on May 16th, but had been getting slower in recent attempts. We agreed for her to give me a 1-minute head start to see if she could catch and pass me by the top. Sort of a reversal of Lifetime’s Battle of the Sexes and without the $200,000 payday to the winner. The prize here was simply local bragging rights.

      +

      + +

      +

      Jana and Jorge Martinez hosted the showdown at their E3 Training Solution's pain cave, complete with Saris H1 and H2 trainers, each fixed atop a homemade rocker board, enough fans to keep the room cool, a shared table to stay on top of hydration and a stable internet connection to run Zwift.

      +

      + +

      +

      Getting in a proper warm-up is crucial before tackling the big climb, and even more so if you choose the shortest route to include the climb, Road to Sky. We mixed in some easy spinning with some hard pick up efforts so it wouldn’t be a shock to the system once we started the climb.

      +

      + +

      +

      After finishing 20 minutes of riding for my warm up I hopped off the bike and blasted my legs with a Theragun to make sure all the muscles were ready to fire.

      +

      + +

      +

      Where to set the trainer difficulty level is up to personal preference. Jana and I agreed to set it at the 50% mark. No matter where in the range you set your trainer you must still produce the watts to power yourself up the climb, but setting it in the lower range certainly smooths out the sting on those super steep pitches.

      +

      + +

      +

      Choosing the right virtual equipment can prove beneficial. For this advice, I asked other Zwifters inside the Forum.

      Ai_1 said, “If you’re not already, use the best climbing bike and wheels. That’s the Tarmac Pro and I think ENVE 3.4 unless you’ve won the Lightweight wheels in the Alpe lottery.”

      Luckily I had previously won the Lightweight Miliensteins and also swapped out bikes on this advice.

      +

      + +

      +

      Titanflexr hit the mark with his suggestion of “fans fans fans.”

      We had three fans blasting us with air, two friends on hand to provide moral support, and Jana’s good luck charm, Trifle the dog.

      +

      + +

      +

      Masnart provided some excellent insight suggesting to “treat this as a race. Put in a mini taper, take caffeine and make sure your carbs are topped off.”

      He added, “if you really want to maximize your e-setup you would use power pedal output as your power source for Zwift, and not the trainer's power source. This helps you gain all of the watts that are lost in the drivetrain of the bike. Could be up to 5 watts.”

      While I relied solely on the Saris H1 trainer for my power reading, Jana used her power meter pedals.

      +

      + +

      +

      How you decide to pace the climb on the early slopes can set you up for failure or success. Jana said she had tried all different strategies: holding back the first half and pushing the second half, even effort, and going all out from the bottom and holding on. Ask a dozen different people and you’ll get a dozen different answers on pacing.

      +

      + +

      +

      With two sub 40 minute ascents up Alpe du Zwift to his name, Mattsurf suggested the following as his go to pacing strategy, “Typically I aim for 95% of my FTP. After each corner I do a mental reset, and go again, repeat 21 times.” He also suggested the helpful tip of having some Coke handy as the caffeine would kick in by the end of the climb.

      +

      + +

      +

      My favorite was the response I got from Kona bike course record holder, Cam Wurf, who blitzed the climb in 36:29 during an intersquad race against his Team Ineos teammates. I asked if he had any pacing tips from his 4th place finish where he was 29 seconds ahead of 4x Tour de France winner Chris Froome. He said, “to be honest I was flat out following Rohan (Dennis) and can barely even recall what the climb was like.”

      Gotta love that guy’s brutal honesty.

      +

      + +

      +

      I’d like to think I threw caution to the wind and used Wurf’s swashbuckling style attacking from the bottom, but realistically my approach was more similar to Kempenaer’s advice to “target your 20 minute max for each and every mini-sector, no excuses until half way. Push hard in the corners so you can reward yourself with a little recovery if your average is still above your 20 minute max. The hurting remains manageable. In the end you might fade a little, but keep targeting your FTP (only allowed after half way).”

      Boiled down, start out hard and hold on for as long as possible.

      +

      + +

      +

      This is how Jana described her pacing effort on the day.

      “My strategy was to go hard from the beginning so that I could try to close the gap to you ASAP. But since that never happened even after the first 20 minutes which I rode pretty close to my 20 minute best power ever I just tried to hold on to whatever I had till the end.”

      She continued, “I focused on pushing the steepest sections even more as that’s where you can gain the most time. I also tried to climb the steep sections out of the saddle instead of seated to change where the pain was coming from.”

      +

      + +

      +

      It helped having a few friends on hand to offer some timely words of encouragement, although it should be noted on this occasion both Gemma Hollis and Andi McKinley were firmly rooted in Jana’s corner.

      +

      + +

      +

      In addition to getting in a good warm up, Jana offers some final suggestions to unlock your potential on Alpe du Zwift:

      "Stay in the moment and focus on one turn at a time. Try not to think about how much of the mountain is left. If you have gone up the mountain in the last 30 days, you will see your current best time for each turn on the screen. Try to beat it, even if it’s just by 1 second. It adds up."

      +

      + +

      +

      Some more tips if you choose to head up the mountain: be mentally ready to suffer and have fun. If you need a little breather, do it on the less steep sections and use your power on the steepest inclines as that’s where you’ll gain the most time.

      +

      + +

      +

      Enter a Zwift race that goes up the mountain as this can give you a little extra push.

      +

      + +

      +

      Make sure to use an extra fan or two if you are planning an all out effort.

      +

      + +

      +

      For those curious, I bettered my previous best by over 3 minutes and stopped the clock in 43:53. I managed to hold off Jana, who lowered her best time to 44:19.

      Special thanks to photographer David Reynolds who shares his work on Instagram @DigitalKnightProductions.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Chuck_Wurster_Changed_the_World_for_You_and_Me_7124.html b/site/slowtwitch.com/www/Indoor_Training/Chuck_Wurster_Changed_the_World_for_You_and_Me_7124.html new file mode 100644 index 0000000..a2660ab --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Chuck_Wurster_Changed_the_World_for_You_and_Me_7124.html @@ -0,0 +1,644 @@ + + + + +Chuck Wurster Changed the World for You and Me - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Chuck Wurster Changed the World for You and Me

      + +
      +
      +
      +

      Chuck Wurster died last last week. He was 86 years old.

      Chuck had been the face of Computrainer since I first met him, in the late 1980s. A year before I debuted the wetsuit that transformed that category into something triathletes wanted Chuck debuted, in 1986, the first—or at least the first meaningful—smart trainer for cyclists.

      Some of us were quick to understand the value. Top athletes back then had key workouts to perform leading up to an Ironman, some outdoor—the 6- and 7-hour rides—and then there were the vital, irreplaceable Computrainer workouts. By the early '90s we realized that the smart trainer was the place for the high-intensity work, and seasonality was not an issue. Chuck Wurster's Computrainer was a year-round training weapon.

      +

      Those who find Zwift compelling today are motivated by the sort of interactivity Computrainer introduced almost 30 years ago, with rider avatars on a game-inspired course populated by other avatars. Although those riders were, in the beginning, not representations of real riders, there was a drafting effect, just as with Zwift.

      In 2007, about 1 in 5 Slowtwitchers used Computrainer for their indoor training, and the rest used dumb trainers because that was the only alternative. In 2010 I asked Slowtwitchers what they'd buy for indoor training, cost no object, and over half said Computrainer. When I polled in 2013 a Computrainer studio was among the most requested features they'd like to see in their local bike shops. Virtually all of today's modern fit bikes used Computrainers as their resistance units (and the fit bike industry still hasn't found a worthy replacement).

      Chuck Wurster’s passing is more than just an industry loss to me. Chuck and I were sympatico in our preference for meeting you all at the events you patronized. Whether trade or consumer, Interbike or the Chicago Triathlon, I had a booth and Chuck did too. Chuck loved you all. I always sought him out these expos. I got the sense that he wondered why I always bypassed others to see him. As in, why are you here? What do you need? What can I do? But it was always just to say hi to him, because he was just such a dear man.

      I don't much like the telephone and I think I make a face whenever it rings. But when Chuck was on the other end, that was different. The last time I remember speaking to him was his call, perhaps in April or May, offering me my choice or a Velotron or Computrainer in exchange for helping him broker the passage of Velotron, his company's last brand, to its new owner. Not necessary I told him, but he insisted.

      When I read of Chuck's passing I was surprised, because I had no idea he was 86. His passion, eagerness, and energy suggested a decade younger. I could tell in his voice that he'd been waning over the past year; I don't know why but I suspect it was because the smart trainer world had finally caught up to him. I don't think he cared that much, except for the effect on his longtime employees.

      Chuck was a nicer man than I am. Chuck bent my trajectory in this regard. He was, very simply, the most gracious human I've met in the bike business. He, along with Steve Hed, pulled me toward grace and away from incivility and disquietude. We read about societies that have no word for an idea common to us (goodbye, please, worry, time). I'm well acquainted with my own strident behaviors and moods and, for men like these, it's not that they've overcome these dark bedevilments; they just don't have a word for them. I'll miss Chuck Wurster.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Cobb_s_Move_Into_Stationary_6584.html b/site/slowtwitch.com/www/Indoor_Training/Cobb_s_Move_Into_Stationary_6584.html new file mode 100644 index 0000000..a8f9c54 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Cobb_s_Move_Into_Stationary_6584.html @@ -0,0 +1,605 @@ + + + + +Cobb's Move Into Stationary - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Cobb's Move Into Stationary

      + +
      +
      +
      +

      Is triathlon going up or down in 2017? I don’t know. But here’s one rising category: Stationary. No doubt about that. Cobb Cycling either noticed this or didn’t; is either prescient or isn’t; this company best known for its saddles is either lucky or smart or both by adding stationary to its offerings. (Doesn't Don Park look like he knows something we don't?)

      This should surprise no ardent Slowtwitch reader, because I interviewed Cobb Cycling’s new owner Don Park earlier this year and we got into his background. A little bit of this man’s brain sits inside on a lot of treadmills you might run on via Kimatek, his main company.

      +

      What is this treadmill you see here? It’s branded Cobb. Mr. Park says this is a commercial quality treadmill, heavy duty enough to be in health clubs. It’s got a speed up to 16mph so unless you’re Allyson Felix you’re probably not going to outrun it. John Cobb maintains it will cost around $4,000 which seems to me pretty inexpensive.

      One interesting part of Cobb’s tech is the software, which looks a lot like what VirtualRunner does. That company strikes deals with race organizers, heading out onto a course with a camera on a Segway just before the gun. You buy this video and watch it as you pretend you are the “first Kenyan” in the race. Cobb’s software adds an avatar, changeable, with changeable kit, like Zwift offers. And, the treadmill inclines and declines as the course changes in terrain. Other avatars are added to the course as your friends virtually join your run.

      +

      There are quite a number of courses already, as you can see, and you can add your own. One imagines a Garmin Virb attached to your head or to a bike, with both a video and GPS file. If I understand right this is all you need to create a course like this.

      The true ambition for this tech is a crowdsourced library of courses, user generated, perhaps not unlike Strava with a camera. You send your file to Cobb, which has a deal with an Asian company that enhances the file, adds the elevation synced to the terrain of the video, adds a cheering crowd if you want, whatever floats your boat. I’m a little fuzzy on this. Just, you now have an enhanced run (or ride) -ready course for stationary training for your solo or group virtual experience. (See the video of a video just below.)

      +

      +

      The treadmill has all of what I’ve been asking for over the past couple of years: a beefy, commercial grade inclinable product without graphics on the console, and with a console that swings out of the way. The treadmill is a smart slave and Cobb leaves the graphics, the software, the display up to those who know now to do that (whether it’s Cobb itself, or Zwift Running, or VirtualRunner).

      But wait! There’s more!

      +

      Cobb has stuck it all into a bike trainer too, that inclines just like the newly introduced KICKR CLIMB. The Cobb cycling trainer inclines and declines according to the software’s commands. It is neither a wheel-on nor a direct drive trainer, rather the wheel sits on a concave mandrel rather like a roller. This type of rear (lack of fixed) connection might solve the front dropout issue that the KICKR CLIMB solves in its unique way.

      John Cobb said his company’s new trainer is powered by the rider, with a supplemental assist from AC power. Claimed is the ability to resist 2000 watts, so even if that is an ambitious number by double that’s enough for anybody’s training needs. And, same thing, film the course and if it’s a video synced with GPS the trainer inclines the bike as the course inclines.

      +

      While the treadmill seems a no brainer great value, the stationary bike trainer, expected to be priced at $3,500 to $4,000, will face competition at the $1,200 and $1,600 direct drive trainer price thresholds, especially considering the $600 add-on price for the CLIMB.

      Bear in mind what you’re reading here is the farthest thing from a review. The treadmill runs on 110v AC which surprises me but then getting the most out of a smaller current is what Kimatek does. The treadmill is projected to be for sale around Thanksgiving. The bike trainer’s projected ship time, it’s definitely not proof-of-concept, Cobb is pedal to the floor to get it done this season, but I sense the treadmill will ship before the bike trainer because the bike trainer appears to be entirely new tech.

      We'll keep you posted.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Cranking_Up_the_Eye_Candy_The_Pain_Cave_in_Ultra_4K_7509.html b/site/slowtwitch.com/www/Indoor_Training/Cranking_Up_the_Eye_Candy_The_Pain_Cave_in_Ultra_4K_7509.html new file mode 100644 index 0000000..e57d289 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Cranking_Up_the_Eye_Candy_The_Pain_Cave_in_Ultra_4K_7509.html @@ -0,0 +1,658 @@ + + + + +Cranking Up the Eye Candy: The Pain Cave in Ultra 4K - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Cranking Up the Eye Candy: The Pain Cave in Ultra 4K

      + +
      +
      +
      +

      Time to settle into the pain cave.

      Bike? Check.
      Smart trainer? Got it.
      Big screen TV? Oh yeah!
      Heart rate monitor? Yep.
      Fan? Of course.
      Hardware to run the indoor training software? Ummm...

      +

      There are some pretty nice pain caves out there, with the ultimate having a nice bike on a direct drive smart trainer in front of an Ultra 4K big-screen TV. But, when it comes time to load up Zwift, Rouvy, The Sufferfest, PerfPRO Studio, TrainerRoad or others, we’ll download it to a phone and run it there while running Netflix on the big screen. The thing that binds the modern day pain cave together is the training application, so why short circuit an otherwise awesome training environment because we didn’t have good hardware on which to run our app? Several indoor training applications support an Ultra 4K experience, so let’s geek out and figure out what it takes to get there!

      Before we get too deep into the geekery, let’s be clear that you should be selecting the indoor training software based on what is going to help you with your cycling goals. Some of the better training tools aren’t especially visual - PerfPRO Studio and TrainerRoad immediately come to mind - and can run on a relatively basic device. Of course, not everybody is motivated by the same things. If good visuals enhance your experience and help you look forward to the next workout, keep reading!

      +

      Drawing Lots of Pictures

      At its core, your training application is having your device’s hardware draw and display a series of pictures (frames) as rapidly as possible, measured in FPS (frames per second). For smooth movie or video playback, 24 or 30 FPS is normal. For smooth video game playback, 60 FPS is often used as a reference point, as it sychronizes to the common 60 Hz refresh rate of TVs. In the gaming world, many people aim for much higher FPS ratings than that, as smooth play and reaction times can affect the outcome of the game. In indoor training, we’re not so dependent on reaction times, so 60 FPS is plenty.

      +

      When it comes to the size of each frame, screen resolution has a huge influence on the hardware needs. When you look at what is now a pretty standard 1080p TV, the actual screen resolution is usually 1920 x 1080, which equates to just over 2 million dots being drawn for a single frame. At 30 FPS, 62 million pixels are being drawn every second.

      Ultra 4K ups the ante considerably. An Ultra 4K screen is usually 3840 x 2160. That’s actually 4 times the number of pixels of a 1080p HD TV. At 60 FPS, we’re now looking at close to 500 million pixels being drawn every second.

      Today’s indoor training software applications can be more or less broken down into two categories, which we’ll refer to as “video display” or “rendered graphics.” Most have some elements of each. FulGaz and The Sufferfest fall into the “video display” category - they provide up to Ultra 4K video content with some basic graphics overlaid as a Head’s Up Display (HUD). Most of the processing power is in the display of the video, which is adjusted to your riding speed, but otherwise pre-rendered.

      +

      Examples of our “rendered graphics” category include Zwift and RGT, which are based on a gaming platform in which everything you see is drawn at the time of display. A variety of hardware and software rendering technologies and techniques are used to make the visuals look more realistic. This is a relatively hardware intensive process as the images on the screen are drawn in real-time.

      Rouvy gets a special mention here - their AR (Augmented Reality) routes are unique in how they combine video with an avatar rendered on top of the video. While the video aspect is pre-rendered, the avatar is rendered realtime and synchronized to the video. This is relatively intense, as well.

      Rendering is complicated on many levels beyond just frame rates. The more elements that need to be rendered - backgrounds, surfaces, lighting effects, reflections, your avatar, other riders - the more complicated the computation, and the greater the computational needs. If your hardware can’t keep up, you start to see inconsistent frame rates, artifacts, or other anomolies which may make the game less enjoyable or even unplayable, requiring you to dial back a bit.

      +

      Beyond the Minimum Hardware Requirements

      Now, the beauty of many applications is they have the ability to adjust according to what your hardware platform can do, either by allowing you, as a user, to adjust what you want to see, or automatically scaling it to the level the application determines the hardware can support. Most applications will provide minimum hardware requirements, but also scale according to what you have. For example, Zwift not only considers your monitor/TV resolution, but will adjust several other factors based on your detected hardware to ensure the best gameplay you can get.

      Here’s an example video of what happens when your hardware isn’t quite up to speed:


      In this example, this was an older laptop that actually didn’t even meet the minimum requirements for Zwift. As you can see, the playback is choppy, and details of the visual elements are blocky. It only gets worse if there are more riders on screen. Even at the lowest resolution in Zwift, this laptop could only muster up an average of 10 FPS, with a minimum of 3 FPS. The best it could manage was 19 FPS.

      +

      We want better than that!

      For our purposes, the goal is Ultra 4K at 60 FPS and with all the visuals turned on, but when it comes down to it, how many devices can actually do that? Not many, and it starts to get expensive.

      Wait... for less than $200, you can run Zwift in Ultra 4K on an Apple TV, right? Not so fast. While Apple TV can display Ultra 4K videos, it can’t actually run Zwift at Ultra 4K. The processor in the Apple TV 4K is capable of “sending” Ultra 4K video content to the TV, but it can’t render at 4K. When you run Zwift on an Apple TV 4K, you actually get 1080p and it dials back the complexity - shadows, animations of animals and people in the background, details of signs, reflections, etc., are scaled back in order to provide adequately smooth game play. It’s certainly not a bad experience, but it’s also not true Ultra 4K.

      +

      So what do we need?

      You essentially need to look at a gaming PC - a computer tuned for playing games, which will likely be around the $1000 mark.

      In our quest for maximum eye candy, we can build or buy a gaming PC to be the center of our pain cave. Our goal is to build a PC that can run Zwift at:
      • Ultra 4K resolution
      • The highest visual setting
      • A minimum of 60 FPS
      You can see the components used in my Zwift PC here. It managed to meet almost all of the goals, except the minimum of 60 FPS. The minimum was 55 FPS, although the average was still over 73 FPS, and very playable. The minimum may have come up short either because I had other applications running or when there were a lot of other riders being rendered on the screen, such as in a group ride. Nonetheless, it’s an enjoyable gaming experience and that PC also does double-duty in the fit studio for motion capture.

      To get a better feel for the system we need to buy or build, we can look at the Zwiftalizer benchmarks. If we filter our Zwitalizer results, only 32 benchmarked systems meet our requirements above. Note that games can only make use of so many cores and the amount of RAM also has diminishing returns. The key ingredient is the video card. The amount of video card memory is key for high resolutions such as Ultra 4K.

      +

      So what are our 60 FPS/Ultra 4K/max eye candy minimum requirements?

      Look for a system with the following:
      • A 4 to 6 core processor running at 3.2 GHz or greater
      • 16 GB of memory
      • A video card with 6 to 8 GB of memory such as an the GTX 1660 Super or better from Nvidia, or the Radeon 5700 or better from AMD
      With all that said, here is a recommended Zwift Ultra 4K build based on the newest AMD processors. If you are not into building your own, here is a search on Newegg.com of machines that should meet our specs.

      With the right hardware, you can enjoy a higher level experience with Zwift or other training apps. Enjoy an Ultra 4K ride through Zwift and Rouvy in the videos below (make sure you open them up in full screen mode for the full effect)!

      +

      Cruising Through Titan's Grove (Zwift)

      +

      Titan's Grove, Continued

      +

      The Sights and Sounds of England in Rouvy

      +

      Fuego Flats in Ultra 4K (Zwift)

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/CycleOps_Direct_Drive_Trainer_5789.html b/site/slowtwitch.com/www/Indoor_Training/CycleOps_Direct_Drive_Trainer_5789.html new file mode 100644 index 0000000..c799f05 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/CycleOps_Direct_Drive_Trainer_5789.html @@ -0,0 +1,610 @@ + + + + +CycleOps' Direct Drive Trainer - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      CycleOps' Direct Drive Trainer

      + +
      +
      +
      +

      CycleOps have just dropped The Hammer on the trainer world with the release of its first direct drive smart trainer. Already the pervasive brand in bike trainers, this newest release takes dead aim at the growing smart trainer market.

      +

      It appears to have universal bike compatibility with thru-axle widths of 142mm and 148mm, along with quick release fork widths of 130mm and 135mm. It will be released with dual Ant+ FE-C and Bluetooth 4.0, meaning it should work with all sensors and software platforms available not he market, including Zwift, TrainerRoad, and Virtual Training.

      +

      + +

      + +

      The Hammer has many of the same features and specifications as Wahoo's KICKR while claiming to replicate "real world inertia" better than anyone else. It also may be among the quietest of direct drive trainers at a claimed 64 decibels at 20mph. Unlike the KICKR, it does not come with a cassette.

      The Hammer is listed as weighing 47 pound, with a couple of nice add-ons: an integrated front wheel tray, support legs that hide under the trainer for storage. The Hammer uses CycleOps' proven PowerTap technology for power readings.

      It will be available the fall of 2016, at a competitive retail price of $1199. Slowtwitch will test and report on this trainer between now and its launch.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/CycleOps_H2_Fifteen_Rides_In_7080.html b/site/slowtwitch.com/www/Indoor_Training/CycleOps_H2_Fifteen_Rides_In_7080.html new file mode 100644 index 0000000..2e8011c --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/CycleOps_H2_Fifteen_Rides_In_7080.html @@ -0,0 +1,648 @@ + + + + +CycleOps H2, Fifteen Rides In - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      CycleOps H2, Fifteen Rides In

      + +
      +
      +
      +

      What you see pictured here is my well-broken-in CycleOps H2. I’ve got dozens of hours on both a Hammer and its replacement, this H2. This is my first set of extended remarks about these trainers, to the best of my knowledge.

      I’m a former bike maker. I built my own factories from the ground up, making both bikes and wetsuits. I couldn’t blame offshore contractors for my wetsuits’ failures – I made them all here in my factory, in the U.S. (as is the case with Saris, CycleOps, and Powertap products.) One thing you quickly learn as a manufacturer: Before your products can do the fancy stuff, the sexy stuff, before you can claim, as so many companies cavalierly do, that their products are the “fastest” or “most aero” on the planet, they first have to do the mundane stuff.

      +

      Like, it should not break. Like, Tab A should fit into Slot B.

      You might find this pedantic, but I deal with smart resistance units constantly, either in smart trainers or resistance controllers on fit bikes. I’ve got 8 or 10 of them in my workshop right now performing one service or another. It’s been my misfortune, sadly, to plug something in and it doesn’t turn on. Or, it doesn’t pair. So, pardon if I celebrate when stuff actually works the way it’s supposed to!

      Here are photos here of both my Hammer and H2. I like the look of the H2 over the H1, it’s sort of gunmetal tone-on-tone and that matches my cosmetic sensibilities. But so what? I just want to know how it works.

      This trainer consists of 3 parts: the trainer itself, a power supply, and a front wheel stand, which is a plastic plate the front wheel sits in and honestly whether you use it or not makes no difference. How does the H2 perform the basic duties of a smart, direct drive, trainer?

      +

      Stability and Noise: Is It Robust? I can’t kill this trainer. The H2 has retractable legs, spring loaded, with height adjustable feet, just like the Hammer before it. The concrete slab on which this trainer sits (in my workshop, where I ride) is not level, so, while some other trainers are annoyingly unstable and unquiet both the Hammer and H2 are rock solid. In fact, let me just say that I can’t find a darned bit of difference between these trainers just visually inspecting them. The H2 is the same machined casting, same weight, same quiet performance, to my eye and ear.

      Accuracy: Of the things that matter to me one difference, apparently, between the old and new Hammers is accuracy. The H2 is +/-2%. In point of fact, in my own testing, it might be a tighter range than that. I think you’ll get a very slightly higher reading from, say, Your Powertap P2 pedals or from a crank-based power meter because of the drive train losses between the pedal (or crank) and the hub. Wouldn’t you think? My Rotor 2inPower reliably tests 2 watts higher than my Hammer, and I chalk this up to the 2 watts I suspect I lose as my power flows downstream from the crank to the hub. Maybe this is a coincidence. I don’t know. Just, it gives me comfort when my PMs and my smart trainer appear to agree, pretty much to the watt.

      My colleague here, Tony Vienneau, found the Hammer was less in sync with his power meters in his excellent long term review of that trainer. I can't tell you whether the H2 is more accurate or whether the firmware is just better. But, CycleOps makes this increased accuracy claim about the H2.

      +

      Bugs: I’ve heard that in certain very narrow conditions it’s possible to overheat this trainer. If you can pedal 350 or 400 watts for a considerable distance, up a simulated climb, and then you rest at the “top” of the virtual climb, resting causes the cooling fan to slow down and not cool this considerable heat buildup you’ve generated. In this case – so I’ve heard – the trainer itself is fine, but it disengages (for lack of a better term) until it cools, i.e., you’re soft-pedaling until it cools (you’d know this, presumably, because the light on your trainer would turn red – see more on the lights below). I’ve never been able to overheat it, and I’ve tried!

      If you do ride with both a power meter and Zwift, at least on the Hammer, you’ll note they’re out of sync by a couple of seconds. I get virtually the same reading, just, one is an “echo” of the other. It’s well established that the Hammer, and smart trainers in general, lag a little behind changes in topography on Zwift. But, it’s to me way down the list of annoyances.

      When I got my Hammer (the older of the two trainers), a year ago, I’d get occasional signal dropouts when Zwifting. Watts would go to zero. Somewhere along the way those dropouts just ceased. I’m going to say, from memory, that was maybe March of this year, 2018. My guess is that a firmware upgrade solved this. I’m a dozen or fifteen Zwift rides in on the H2 and I haven’t had that happen to me.

      Does It sync? Does it pick up signals? It just drives me batty when I have to mess around with electronics and software that don’t pair, or signals that drop. The H2, like the Hammer, is just plug and play, and transmits both an ANT+ and BLE signal. If you’re a Zwifter, which I am, both the Hammer and the H2 always paired with Zwift as soon as I started pedaling. I had to do… nothing.

      +

      +

      I shot this little one-minute video above just to show you what it looks like when you boot Zwift on a Windows laptop and then the video starts when I log into Zwift. I’m “pedaling” by turning the crank with my hand, so, it takes a little longer than usual. Just, this is it. This is pairing with my H2 and Zwift. The 3 "devices" Zwift picks up are just the 3 signals my H2 sends out that Zwift needs: Cadence, Power, and a Controllable Trainer. (There are 5 "device" screens in Zwift; it could also have picked up a HR monitor, but I wasn't wearing one, or a Speed Sensor, but that would have been superfluous.)

      Portable? Yes. Sort of. It’s got a 25lb flywheel built into a 50lb overall product. It’s got a handle built into its machined casing and retractable legs, so it’s heavy but not cumbersome. I want all that weight, both for performance and stability. It’s easy to take with you if need be. But I don’t exactly look forward to transporting it.

      Other stuff: This trainer has lights, blue red and green, and white, flashing and solid. Here’s a list of them. Red means there’s a problem; green means powered up but not in service; blue is for Bluetooth; white is for ANT+. Most of the time when I’m Zwifting it’s the blue light if I’m using my Mac laptop (because I’m using Bluetooth); or white if I’m using my Windows laptop (because I’m using my ANT+ dongle in that case).

      Sometimes I’ve got a tri bike on the trainer, and that bike is a disc brake bike with a thru axle. Sometimes it’s my road bike with a standard quick release. Don’t go throwing stuff out if you get this trainer. It comes with end caps (as most wheels come with nowadays) that transform the hubs into thru axle or quick release. Just keep the end caps so that your Hammer is ready for either/or. If you look at my trainers, the Hammer is set up for QRs, the H2 for thru axles.

      You can find blems and closeouts on the Hammer. If you can find them for under $1000, do it! It's a great trainer when you upgrade the firmware. Otherwise, this trainer, the H2, sells for $1,199, which is a very fair price for a trainer this stable, this accurate, relatively bug free, proven, and that can take 2000 watts and a 20 percent simulated climb. If you're interested you'll find more here about the CycleOps H2.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/CycleOps_Hammer_Long_Term_Review_6570.html b/site/slowtwitch.com/www/Indoor_Training/CycleOps_Hammer_Long_Term_Review_6570.html new file mode 100644 index 0000000..a7e2443 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/CycleOps_Hammer_Long_Term_Review_6570.html @@ -0,0 +1,608 @@ + + + + +CycleOps Hammer: Long Term Review - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      CycleOps Hammer: Long Term Review

      + +
      +
      +
      +

      CycleOps has been producing quality indoor trainers for a number of years and the Hammer is its first entry into the growing direct-drive smart trainer market. Entering the market after the already established Wahoo Kickr and Tacx Neo, the Hammer has its work cut-out, but with the well-established reputation in the industry for quality trainers and power meters, it shouldn’t be a surprise the Hammer is another solid addition to the smart trainer market.

      Technical Specifications

      The Hammer is in the same class of the direct drive smart trainers as the Wahoo Kickr, Tacx Neo, and few others. Its specs and design are on par with others in this cohort. It sports a 20lb flywheel, uses electromagnetic resistance, and is rated for up to 2000 Watts, 20 percent climbing grade, and a claimed power accuracy of +/- 3% with internal temperature control.

      +

      It may well have the widest compatibility of all the trainers on the market, supporting dual BTLE 4.0 and Ant+FE-C, along with being compatible with the growing number of thru-axle frames. The Hammer works with fork widths of 130mm, 135mm, 142 x 12mm, and 148mm x 12mm, with included adapters. Thru axle adapters are aftermarket purchases for both the Tacx Neo and Wahoo Kickr. The distance between the cassette and the body of the trainer measures about 7mm and is approximately 13.25 inches from the ground to the QR skewer.

      Size? It is a beast weighing in at 48.3 lbs on my scale with a cassette, and has an extended footprint of 48cm in height, it’s 49cm long, and is 78cm with legs extended (21cm folded). This makes it the widest in its class and may be the most stable. It comes with an integrated handle but this trainer (like the Tacx NEO) will not be the choice for a travel trainer.

      +

      Getting Started

      The CycleOps Hammer is nearly ready to go out of the box, with the exception of a user needing to supply their own cassette and QR skewer which are not being included. Once the cassette is installed, the legs on the Hammer are easily opened by pushing upon small yellow levers on each side of the Hammer and pulling the legs forward from underneath. The Hammer legs also have adjustable legs for any uneven surfaces.

      Underneath the trainer, hidden with the legs, is a front wheel tray. I loved the idea of the integrated storage for the tray however it is not needed for the and since it is made of thin plastic with no rubber backing, it moved a bit to freely for my liking during use.

      Once the Hammer is powered-up, there are LED lights located on the left side of the unit that change color based on its status. At the initial start-up, the LED’s are blinking green and once connected to an Ant device they change to white in color while BTLE devices will show as a blue LED. There is a red light that indicates an issue with the system but I’m happy to say I have not witnessed it.

      +

      Once powered, I’d recommend pairing the Hammer to the free iOS PowerTap mobile application for firmware updates or their mobile training app, Rouvy (formally VirtualTraining) which can function as a training tool, and allow for firmware updates, perform a zero-offset, and calibrate the trainer. The Rouvy app, available in both iOS and Android, is free for basic use but a subscription is required for some of the more advanced features. There is also the option to update the firmware via their online software (Rouvy) platform on a desktop PC.
      Software

      As with its competitors, the Hammer worked smoothly across numerous training platforms and devices. I tested the Hammer on TrainerRoad, Rouvy, Zwift, along with some others and it performed on par with other direct-drive smart trainers. Using Erg mode, I found the power fluctuated a little more with the Hammer than with the Neo and Kickr but the average wattage for an interval was in-line with the set wattage, albeit elevated. Also, the Hammer was excellent at releasing and re-acquiring a set wattage or slope during an interval, or virtual racing on Zwift, after stopping. Instead of trying to push 400 watts, or on a 15 percent incline Zwift hill, from a dead stop, the Hammer lets the user gradually build up effort before returning to full erg mode.

      I also did not have problems recording data via Garmin devices (Fenix 5x and 520) and Wahoo devices (Elemnt and Bolt). The Hammer broadcasts both power and speed, however it does not broadcast cadence.

      Of note, the recent update to Zwift allowing for calibrations of power meters does not yet seem to work with the Hammer while it worked successfully with the Rouvy and TrainerRoad.

      +

      Power, Sound, & Vibration Testing

      After a 20-minute warmup and zero offsets I compared the claimed power accuracy of the Hammer (+/- 3%) versus the PowerTap P1s (+/- 1.5% claimed) and the Pioneer 9100 series power meter (+/- 2% claimed) at a number of intervals including 1000+ watts, 300 watt efforts and recovery at 150 watts. Such comparisons are not necessarily apples to apples but they can provide some clues towards tendencies.

      +

      At extreme wattages, either high or low as shown in the above two graphs, the Hammer measured noticeably, and constantly, higher than both the Pioneer and P1s power meters.

      +

      The good news, at more realistic power outputs and steady state efforts, these differences became smaller. The above two graphs are 200 watt intervals on TrainerRoad, the top graph using the Hammer and the bottom one using the Neo. The Hammer, although steady, showed elevated watts (15 watt interval average) versus the Neo (4 watts) compared to both the P1s and the Pioneer power meter.

      Now, these power differences may also be moot with many training platforms offering the ability to match your power meter to your trainer.

      I also tested the noise made by the Hammer versus the Tacx NEO and Kickr 2.0 under the test conditions of riding at approximately 200 Watts for one minute at 75-80 rpm, a freshly lubed drivetrain, a straight chain line, and on a bike trainer mat. The ambient room noise was 41db in the room. The Hammer averaged 64db, the Tacx NEO averaged 59db, and the Kickr 2.0 averaged 63db.

      An important caveat for this testing is that I was required to remove the bike from the trainer for each test so that comparing the numerical results may not be entirely accurate when there is only a 1db difference between the Kickr 2.0 and the Hammer and subjectively the difference between the Kickr 2.0 and the Hammer was not noticeable but the Neo did remain King on these tests.

      +

      Vibration is a topic often mentioned as influencing the sound made by indoor trainers, particularly for individuals who live in apartments. The above image shows vibration at 200 watts using the Tacx Neo while the below image is 200 watts using the Hammer, both on a hardwood floors. Although the Neo is quieter, it was the Hammer that appears to produce less floor vibration, both in terms of the data and feel on the bike. This was measured using a mobile application.

      +

      Finally, the Hammer does require an electric current to be used. Without power, the Hammer tops out at about 50 watts, similar to the Kickr, whereas the Neo can reach about 220 watts without power.

      Final Thoughts

      The Hammer is one of the most stable and sturdy trainers on the market. At a cost of $1199, it is on par with its main competitors and compatible with nearly every bike on the market. Although it’s power accuracy was lower than some other direct-drive smart trainers, which may not even be a consideration if the bike you ride on your trainer has its own power meter, its smooth road feel made up for this shortfall.

      +

      Read more about The Hammer.

      [Note: If you look closely you’ll see on the graphs above the watermark of our estimable colleague DC Rainmaker. We are not, as it might seem, using his work. These are our numbers analyzed on a piece of software we purchased. This company developed the software in concert with DCR, hence it bearing his watermark.]

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Delightful_Masochism_Playing_on_a_Screen_Near_You_7241.html b/site/slowtwitch.com/www/Indoor_Training/Delightful_Masochism_Playing_on_a_Screen_Near_You_7241.html new file mode 100644 index 0000000..2a75f48 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Delightful_Masochism_Playing_on_a_Screen_Near_You_7241.html @@ -0,0 +1,646 @@ + + + + +Delightful Masochism Playing on a Screen Near You - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Delightful Masochism Playing on a Screen Near You

      + +
      +
      +
      +

      I debuted my first Zwift workout on a bunch of early adopters on Tuesday, during our Tuesday Structured Training. Three bad things happened: I dropped off the screen. Like the Vanisher (did you see Deadpool 2?)

      Second, Zwift had a momentary bug (fixed now) that rendered a number of folks unable to ride in ERG mode. Third, I just think my workout was too easy.

      So, I wrote another, it's called Delightful Masochism, and I'll be leading the ride again. Jesse Thomas, our usual leader, is nursing an orthopaedic thingy. So, it's me for a couple of weeks.

      +

      Leslie Knibb is back after a week of R&R for Ride of the Valkyries with a new workout. We're noodling a second women's ride, later in the week. The Womanifesto. I got it from Janet Jackson, spoken by her during her Rock & Roll Hall of Fame induction. I thought it would make a great name for a Zwift women's ride and the leader for that ride (should it make it on the schedule) agreed.

      Hilly Vanilli? That's Thursday afternoons. Dang, I love that ride. And dang, I got spit off the back at 22 minutes. But it was my 2nd workout of the day and I'm trying to get my fat carcass in shape. Sometimes that happens.

      I think our most populated ride is the Ride & Grind, for U.S. East Coasters, Europeans. 5:30am Thursdays.

      +

      Lots of Slowtwitchers ran with Gwen Jorgensen on her birthday last Friday. Gwen was gracious as always, and I think about 40 showed up. Our own Herbert Krabel, Mr. SwimRun, who started life as a professional MTB racer, he made some people suffer in the last mile!

      +

      I keep saying this, but I really think this is the last week we do NOT have several runs on our Zwift calendar. Just need to pick up a few more leaders. Oh, and the new course, that looks like Moab, or Monument Valley. Good going, Zwift! It's a treat for the eyes.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Do_the_NEO_Bike_and_the_Garmin_Vector_3_Power_Numbers_Match__7637.html b/site/slowtwitch.com/www/Indoor_Training/Do_the_NEO_Bike_and_the_Garmin_Vector_3_Power_Numbers_Match__7637.html new file mode 100644 index 0000000..800e1ad --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Do_the_NEO_Bike_and_the_Garmin_Vector_3_Power_Numbers_Match__7637.html @@ -0,0 +1,646 @@ + + + + +Do the NEO Bike and the Garmin Vector 3 Power Numbers Match? - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Do the NEO Bike and the Garmin Vector 3 Power Numbers Match?

      + +
      +
      +
      +

      Because I have both a Tacx NEO Bike and a set of Garmin Vector 3 pedals, I thought I’d see how well their power numbers match. This has been discussed a lot in the past, by DC Rainmaker, GPLama, and on the Garmin and Reddit forums and on our Reader Forum. But I haven’t seen much recently. I did see a recent review on Cyclist magazine that reported the Vector 3 highly accurate, and I know that this has not always been the response of reviewers. GPLama (Shane Miller) by his own reckoning has weathered a lot of “drama” with this product, but about a year and-a-half ago he found a new set of Vector 3 pedals quite acceptable (this, a year after his first go at the pedals). So I thought I’d give it a go.

      It would be embarrassing for a company making both power meters and smart trainers to suffer big discrepancies between the two, when the two products are used together. To my knowledge Saris escaped ignominy. Its Powertap P1 pedals and Saris’s smart trainers (e.g., the H3) were pretty well spot on according to what I’ve read. Saris sold Powertap to SRAM. Meanwhile, Garmin bought Tacx, so, now this is the company making both products.

      I love how GPLama, in his podcasts and blog posts, works his way through problems in real time. He asks the questions and then sometime later in the video or the article answers them. He works through them just like you and I might. But usually a lot quicker than I might. An example of Tacx/Garmin’s lack of congruity between the power meter and the trainer, juxtaposed with Saris’s congruity, can be found in a very early (in the Vector product cycle) GPLama blog post.

      +

      That blog post was in 2017, before Garmin purchased Tacx and Saris offloaded Powertap. To find out how things are now between each of Garmin’s flagship power-producing/measuring products I used the DCR Analyzer, just as Shane did, except in this case I tested Garmin’s Vector 3 pedals against Tacx’s NEO smart bike rather than the standalone NEO smart trainer. I love this bike! This and Wahoo’s KICKR Bike have set the new industry standard for stationary training and I contrasted the two a couple of weeks ago.

      I won’t belabor this. There’s the chart, above. The NEO Bike and the Garmin Vector 3 are near spot on. I tested this over 4 rides, and that chart is from the second-most recent ride. The data shown is for the average of the entire ride, about 40 minutes, but the graphed part is truncated due to the limitations of the architecture of our article portal (the DCR Analysis graphs are extra quite). Be aware that I’m not testing through the range of power that GPLama does. He’s testing up to and above 1000 watts. I’m testing to whatever I get to during a ride, in my normal course of riding, which might for a short time, maybe, hit 400 watts if I get a wild hair.

      +

      The average watts for the ride are about the same between the pedals and the trainer and I mean within a half a percent between the two. What about 10sec and 3sec averages throughout the ride? No. That’s where the congruity ends. I measured this by taking the DCR Analysis and filtering down to 3sec and 10sec segments throughout the ride. The precision is not there when I attempt to dial up the resolution. Why? If you look at the graph above, there’s a bit of smoothing going on with the NEO Bike that you don’t see with the pedal. I got the pedal’s .fit files from the pedal, and in the case of the NEO Bike I downloaded the .fit files from Zwift. It isn’t Zwift that’s doing the smoothing. They present the data as they get it.

      Why do I find congruity between the NEO and the Vector 3 today, since congruity among these products hasn't always been in evidence? The products are different then than now. GPLama, in the 2017 video I linked to above, was running firmware version 2.30 in the Vector 3 pedals and I’m running 3.80 today.

      I asked Tacx about differences between the NEO bike and the trainer. I also asked whether one ought to expect the power shown by the pedal to vary from the resistance produced by the trainer, because, someone stomping in a Garmin Vector pedal would have to overcome the resistance produced by the NEO trainer and the mechanical inefficiency of the bike and the drivetrain, it seems to me. The NEO Bike’s drivetrain is virtual. Or has Tacx accounted for this in the way the trainer measures power and produces resistance? I don’t know. I asked. When they tell me I’ll tell you.

      +

      Above is a graph of my last ride. Yesterday. There’s a pretty big variance between the Vector 3 pedal and the NEO Bike. As you see, there’s about a 2.5 percent difference between the bike and the pedal. (You might not think this is a big variance, but I've gotten use to the Vectors matching, to the watt, what Zwift displays for the ride, and Zwift is getting its numbers from the NEO Bike.)

      Why this variance in my ride yesterday? I can’t be sure, but this is the one ride where I didn’t zero offset calibrate the pedal before I started the ride. So far, whenever I calibrate before the ride the numbers sync up nicely. When I don’t, I get this variance. If you're new to power meters, don't get freaked out by this. You push the power button on your GPS head unit and it will ask you, do you want to calibrate the pedal? Yes. You place the crankarms horizontal to the ground (which is different than, for example, Shimano, where as I recall the crankarms are to be situated vertical to the ground). You press calibrate. Ten seconds later you're ready to ride.

      Why am I even writing this kind of thing, when GPLama and DCRainmaker both do it so much better, and more thoroughly, and with more background, than I can bring to bear? Two reasons. First, because I’m an average person, and if you’re average too sometimes it’s nice to hear from a peer. Second, I have a lot of interesting stuff in my workshop all at one time, right now, temporarily: a NEO Bike; a KICKR Bike; several power meters, including the notorious Dura Ace. I therefore shall, as they say, make hay while the sun shines.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/ERG_Mode_in_Zwift_7107.html b/site/slowtwitch.com/www/Indoor_Training/ERG_Mode_in_Zwift_7107.html new file mode 100644 index 0000000..30e0550 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/ERG_Mode_in_Zwift_7107.html @@ -0,0 +1,646 @@ + + + + +ERG Mode in Zwift - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      ERG Mode in Zwift

      + +
      +
      +
      +

      Zwift offers several types of events, as you know: rides, races, workouts. You can do solo workouts, just as in other stationary platforms. Or, do your workouts inside the camaraderie of a group. There are several of these on the Slowtwitch Calendar, one of which I regularly participate in: Slowtwitch Structured Training at 4pm Pacific Time. We just launched a new event, women-only group workout, Ride of the Valkyries, Tuesday mornings. (Not a Zwifter? Ready to take the plunge? Click here.)

      It’s almost impossible to get dropped from these rides. However, sometimes it’s almost impossible to keep pedaling during the ride. How can both things be true?

      It’s hard to get dropped because these workouts are effort based, not talent based. If the workout is pegged to some percentage of your FTP, then if my FTP is 200 watts, and your FTP may be 350 watts, we’re riding side-by-side as long as we’re both riding at, say, 90 percent of our FTP. (FTP is what you can hold, in watts, on the bike for about an hour.)

      +

      Enter ERG Mode, and, as I explain ERG Mode I’ve included screenshots of how I toggle in and out of ERG Mode both in The Game (the Zwift program you’re in when you’re riding) as well as in the Zwift Companion App. If you’re confused already, I explain this below.

      ERG Mode became popular among Computrainer users a quarter century ago. You tell you resistance unit on your stationary trainer to “make” you pedal 250 watts. Power is torque x RPM. So, if my cadence drops, my Computrainer then, and now Zwift today, recognizes this cadence drop and increases the resistance, so that the power I’m producing remains 250 watts.

      This works great. I use ERG Mode in bike fitting. I make a change in your position, and you and I decide which of two positional options (e.g., handlebars slightly higher or lower) is best if you need to continue to pedal 250 watts. (In fact, with the demise of Computrainer and few other good options, I’m investigating a bike fitting protocol that uses Zwift controlling the other resistance units we're putting on our new fit bikes, such as those by Kinetic.)

      Our structured training workouts on Zwift are much like the workouts you’re riding on any of a number of stationary software platforms. They’re interval workouts, so, 2 minutes at 120 percent of FTP, 1 minute at 60 percent, that sort of thing, for an hour.

      +

      Sometimes I’m just plum tuckered. I’m not having a stellar day. And, maybe the ride leader decides to give us a rather tough workout. My cadence drops to 80, torque goes up. My cadence drops further, to 70, torque goes up. This spirals down until I just can’t pedal the bike any longer. What do I do? When I first started Zwifting, I just sat there, dumb, on my bike, not pedaling. Today? I just drop out of ERG Mode.

      There are two ways to do this. I always have Zwift, the program – AKA “The Game” to seasoned Zwifters – running on a particular computer, which has been my Apple MacBook Air, then changed to a Windows 10 laptap, and just last week I started running it on a new 4k AppleTV. The two screenshots above I took using the camera on my iPhone, while pedaling. They're of a new 43" big screen smart TV (sounds expensive but I bought it for under $300 at Best Buy) running The Game loaded onto AppleTV ($179 at Best Buy).

      Then, separately, I run a thing called the Zwift Companion App on my iPhone (this runs on any Android or iOS device). I use the Companion App to message people, and to give Ride Ons!, because it’s easier to wrangle my iPhone (including speaking into it) than to try to type on a laptop or mess with AppleTV’s remote when I’m riding. You can toggle yourself in and out of ERG Mode either inside The Game or on the Companion App.

      You might say, “That’s cheating! You’re cheating the workout!” And you’d be right, I guess. It’s more like sitting out a 50, in Masters Swim workout. The workout just got too hot. There’s another explanation, of course. I have my FTP set too high! (You tell Zwift things about yourself in your Zwift profile, like your weight, your gender, and your FTP; if you find yourself continually needing to bail on your structured training workouts, perhaps you've "told" Zwift your FTP is higher than it currently is.)

      What is most often the case, however, is that the job of leading events (which I must sometimes engage in) sometimes conflicts with the personal imperative to complete certain challenging workouts. I can’t, for example, easily talk to you all during a stiff workout and also concentrate on riding a hard effort myself. As we’ve moved from 2 weekly Slowzwift events to 5 in recent weeks, with more coming, this is a message I give our ride leaders as we bring more of them aboard – chaining yourself to ERG Mode for an entire workout may make it hard to properly lead a workout.

      +

      The screenshots highest above show me bringing up the navbar on the bottom of The Game by clicking near the bottom of the screen. You see ERG Mode to the left on the navbar. Simple. Click and it’s off; click again and it’s back on. The second screenshot above shows me riding with ERG Mode off. I’m pedaling below the power the workout is asking for. But I’m not getting dropped from the group, because that’s the nature of group workouts in Zwift (it’s hard to get dropped).

      Then, just above, there are two screenshots from my iPhone, side by side, running the Companion App. There’s a blue navbar menu across the bottom, and in the “Workout” pane, that’s where I click ERG Mode on and off. In the screenshot on the left, you can’t find ERG Mode. That’s because it’s off; you see “Incline” there (more on that some other time). The screenshot on the right is what you see if you click where it says “Incline”. If you click there, “Incline” is replaced by “ERG Mode” and now you’re in ERG Mode.

      What happens when ERG Mode is off? Phew! I’m just riding along, at whatever power I’m riding, untethered from the requirement of the workout. As long as I keep pedaling even a somewhat reasonable amount I remain tethered to the group. I can drop back in to ERG Mode at any time. (If the workout calls for a hard effort, and you see me tooling along at 1.5 watts per kilogram, you know I’ve dropped out of ERG Mode.)

      I write this because I want you busy this Winter. I want you engaged. Whether you choose to take part in our 100/100 Run Challenge, in a VASA strength-building regime to make you a better swimmer, The Sufferfest and Tour of Sufferlandria, Trainerroad, Rouvy, or Zwift, I want you engaged. Eager. Happy. Not discouraged. Retreat is the better part of valor, and I’d rather have you retreat out of ERG Mode on days when you just don’t have it rather than the workout forcing you to pedal to a stop.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Elite_Direto_6632.html b/site/slowtwitch.com/www/Indoor_Training/Elite_Direto_6632.html new file mode 100644 index 0000000..f083361 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Elite_Direto_6632.html @@ -0,0 +1,608 @@ + + + + +Elite Direto - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Elite Direto

      + +
      +
      +
      +

      In a tough market dominated by Wahoo, Tacx, and CycleOps, the Elite Direto is not the most feature-rich, nor the quietest, nor perhaps the smoothest, but it is certainly a bang-for- buck smart trainer finalist.

      Technical Specifications

      The Elite Direto delivers on the required imperatives for compatibility. It supports Ant+ and FE-C, and BTLE 4.0, and is compatible with nearly every bike frame on the market, including those with thru axles, and with dropout widths of 130mm, 135mm, 142 x 12mm, and supports 9/10/11 speed cassettes by SRAM, Shimano, and Campagnolo (with adapter).

      +

      The Direto uses a 5-foot power cord with a thin cable housing that plugs into the front of the trainer. The cord is noticeably short when compared to the 9.5-foot power cord that is standard with the Neo and Kickr and I have read of a couple cable cords being accidentally damaged that might not have been with a sturdier power cord. Without power, the Direto will deliver 200 watts of resistance.

      The body of the Direto is constructed mostly of hard plastic, with an integrated handle, while the legs are hollowed steel and held in place via a nut-bolt system, instead of a spring based system found in most other direct drive smart trainers. The result is a trainer that weighs about 33 lbs, or 15lbs lighter than the Kickr or Neo.

      The Direto does not come with a riser block; when attached to a TT style bike, the front end will have a drop of about 1.5 inches. You must provide your own cassette. The flywheel and cassette sit about 15mm from each other, and I didn’t have any clearance issues with rear derailleurs.

      +

      The Direto uses an Optical Torque Sensor power meter with a claimed accuracy of +/- 2.5% and can provide L/R power balance and other advanced pedaling metrics with the use of its external cadence sensor and Elite software. The Direto can simulate slopes up to 14 percent; a maximum power output at 40km/hr of 1400 Watts; and uses a 9.2lb flywheel. With the exception of the power metrics, the Direto specifications are considerably less when compared to the KICKR, Neo, or Hammer which boast 20% slopes, 2000 Watts, and 20lb flywheels.

      Though the Direto comes up a bit short of its competitors, its decision to omit the gaudiest of überspecs may be a strategic win at the cash register. While a lighter flywheel may not deliver a ride quality the equal to flywheels considerably heavier, are you worried that you might max out a trainer with a 1400 watt upper limit? Or will ride a simulated route with inclines greater than 14 percent?

      Getting Started

      The Direto required very little assembly: Install the three legs with the supplied bolts and Allen key wrench, and slide on a cassette. As mentioned, the legs are not spring activated so they require tightening a bolt for the desired position. The three legs are not individually length adjustable, so this trainer is not optimized for use on uneven surfaces.

      +

      Once the Direto is powered-up there are three LED lights on the unit that indicate its status. At the initial start-up there are green- and blue-blinking LED’s: Blinking green means it’s awaiting a connection to Ant+ sensor; blinking blue means it’s looking for a BTLE to pair with. The red LED just means the unit is powered.

      Once powered, I’d recommend pairing Elite’s free mobile training applications available in both iOS and Android. Although one should be able to do a spin down via other means (e.g. bike computer, Zwift), I found some early software glitches made this difficult and the best means was via their own application.

      Software Testing

      The Elite Direto is compatible with a bunch of training platforms and bike computers. I tested the Direto on TrainerRoad, Rouvy, Zwift, and its mobile application and it performed quite well. I did notice that the Direto is slower to react to changes in power or grade versus some of the other smart trainers and there is a hill on Zwift that is beyond its 14% maximum grade. To be fair, a user who is not testing on various other smart trainers on a regular basis probably wouldn’t even notice the difference in feel or smoothness but it is there nonetheless.

      +

      I did not have any problems recording data using Garmin devices (Fenix 5x and 520/1030) and Wahoo devices (Elemnt and Bolt). The Direto broadcasts power, cadence, and speed. There are some software glitches (see above) in speed and cadence recording for some bike computers (Elite says its working on these) but this does not appear to be affecting virtual racing platforms like Zwift.

      Power, Sound, & Vibration Testing

      In order to be consistent in testing indoor trainers, I have a dedicated workout for trainers. I test across a spectrum of wattages and intervals, trying to focus on the most common intervals (200, 250, 300 watts) and then throw in some hard efforts (1200 watts) and easy efforts (100 watts) where differences can be extreme.

      +

      In this test, I tested against the PowerTap P1’s and the Pioneer power meter which have been the most consistent and are generally within a couple of watts of each other (Note: Pioneer averages 3-5 fewer watts than the P1 pedals at the same power output).

      +

      After a 20-minute warm up, I ran a spindown test on the Elite and zero offset for both the Pioneer and P1. The correlation of the results was extremely impressive, minus some cadence issues. In fact, although the Direto has a claimed accuracy at +/- 2.5%, I’d say that it is closer to +/- 1%.

      +

      I also tested the sound made by the Elite Direto using the SPLnFFT application. I repeat the same test conditions for each trainer by riding at approximately 200 watts at a cadence of 75-80rpm on a freshly lubed drivetrain. The Elite tested 4db louder than the Tacx Neo, the same as the Wahoo Kickr, and 1db quieter than the CycleOps Hammer. However, under higher wattages (300+ watts), the Direto suddenly becomes the loudest of the group which is likely due to its open flywheel design.

      +

      Vibration testing using produced similar results compared to other direct drive smart trainers with the same caveat for the Elite at higher wattages. The image above shows vibration at 200 watts using the Tacx Neo while the second image below is 200 watts using the Elite, both on a hardwood floors.

      +

      Final Thoughts

      The Elite Direto may be the most practical direct trainer on the market. At a retail cost of $899, about $300 below the Kickr or Hammer, it provides equal or better power accuracy with the same bike and software compatibility. If you are not riding over 1400 watts and can settle for inclines of less than 14 percent, the Direto may be a financial win over the Kickr and Hammer.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Every_Thursday_it_s_Jimmie_s_Day_7644.html b/site/slowtwitch.com/www/Indoor_Training/Every_Thursday_it_s_Jimmie_s_Day_7644.html new file mode 100644 index 0000000..88292e6 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Every_Thursday_it_s_Jimmie_s_Day_7644.html @@ -0,0 +1,648 @@ + + + + +Every Thursday, it's Jimmie's Day - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Every Thursday, it's Jimmie's Day

      + +
      +
      +
      +

      By design, Jimmie Will Richey’s work week ends just before the Hilly Vanilli begins. Jimmie leads the "D" group in this Zwift event on the Slowtwitch calendar and his ride leadership has become legendary, like an exuberant dancing intersection traffic cop.

      Leading the Hilly Vanilli “Ds” “has become the highlight of my week,” he says and his leadership makes this ride the highlight of a lot of peoples' weeks.

      +

      “I have made sure my schedule allowed me to be off on Thursdays because I like this ride so much,” said Jimme, and it’s not an easy schedule. “Every Spring and Fall we refuel the nuclear power plant at which I work.” Jimmie’s worked at this plant for 30 years, and during the refueling of the plant Jimmie works 6 days a week, 12-hour daily shifts. It’s like this for up to 6 weeks every Spring.

      “I am 58 years old and I work as a component engineer at a nuclear power plant, near the gulf of Mexico, south of Houston Texas. I have been an avid Zwifter for 2 and-a-half years and cannot get enough of it. I am married and the proud father of two great women who have grown up to find wonderful partners to spend their lives with. My greatest feeling of self-satisfaction and accomplishment in life is thinking I had something to do with the way these kids turned out. They make me happy every day.

      +

      You can divide the human race into the net givers and the net takers and over the course of one Thursday afternoon/evening you quickly find out that Jimmie is a giver.

      This is one reason why this event, which offers has B, C, and D levels, is overwhelmingly populated by Ds. About sixty Ds came out last week and it’s largely because of Jimmie.

      I was frankly surprised to find out Jimmie is 58 years old, because his leadership of the Ds, at least his version of leadership, often means dropping back to pull someone back up to the group. Jimmie rides a C-level effort to lead the D ride. Of course he’s a fit and trim athlete as you can see from a recent picture. But this is the recent Jimmie Will Richey. He’s half the man he used to be.

      +

      As Jimmy Buffet sang it, Jimmie Will Richey was the “Son of a Son of a Sailor.” His father was in the U.S. Navy in World War II, and Jimmie spent north of 8 years in the Navy. Because of the transitory nature of the military family life Jimmie changed schools a lot as a kid and did not cope well. “Early on I turned to food and books for companionship.”

      “I weighed 310 lbs when I finished High School and at 5’9” tall that was quite a bit of fat.” Jimmie shed 100 pounds upon joining the Navy. He gained and lost that amount of weight twice during his Navy career. When he left active service he grew to his heaviest; by his “32nd birthday I weighed 350 lbs,” meaning there were two Jimmies inside of one skin.

      He’s been at his current 175-pound weight for about a dozen years now. He learned to swim in his youth, and during his big weight loss season of 2007 took up cycling. “I was into road cycling when I got asked to help someone with a bike for a triathlon. I fell for the challenge. I loved riding, I knew I could swim again, but running was brand new to me.”

      +

      He moved from Olympic to the Ironman discipline. But in more recent years, “the shale oil and gas boom around my area ruined the local roads. Combined with distracted drivers those two factors led me to spend more and more time indoors. In 2017 I read about an online game called Zwift. I had a Powertap hub, a Kurt Kinetic fluid trainer and a computer so I thought, ‘Why not?’ Shortly thereafter I got a Cycleops Hammer which I still use.”

      “I subscribe to the Slowtwitch newsletter email and a year or so ago you put out a call for leaders. I volunteered for the Hilly Vanilli D group not knowing what to expect. The only thing I knew at the time was it said, 2.0 – 2.5 watts-per-kilogram (a typical Zwift measure of work) and I knew I could do that and still communicate with the riders. The first ride I led there, it was just me but people started showing up and coming back after a while. We now have a great group of regulars. We are not as big as some groups but I tell you true, that ride makes my week.”

      “Zwift has become my means of cycling. I have plans to ride outside again after this pandemic ends but for now my PC, Hammer and Zwift give me all the fun I need.”

      +

      What you see here are screenshots from my iPhone of Zwift’s Companion App. It’s the only way to get a transcript of the in-game messages during a ride. It is hard to message in-game when everyone is near his or her limit trying to keep up. The easiest way to illustrate the effect of Jimmie’s leadership was to just snap some screenshots of his messaging. The volume of Jimmie's in-game message help is remarkable.

      For those interested in this ride, Jimmie’s D group leaves at 5 minutes before the hour, at 3:55pm Pacific, 6:55pm Eastern time. At the top of the hour the C group leaves, and 5 minutes after the hour it’s the B group. The ride for all groups ends at the same time, at the top of the next hour, so each group rides 65 minutes, 60 minutes, or 55 minutes respectively. The Bs try to catch the Cs, who try to catch the Ds (imagine those charts with a bigger fish poised to eat a smaller fish who’s poised to eat a smaller fish yet). Jimmie’s ardent effort at keeping the D group working together is not just for camaraderie and teamwork for teamwork’s sake, it’s to help the D group in its mission to keep from getting caught.

      “A side effect of this ride is I have rubbed internet shoulders with the stars,” and a case in point is the occasional communication about this ride with Jordan Rapp. But on Thursday nights, for a lot of Zwifters, both Slowtwitchers and those who've never heard of Slowtwtich, Jimmie is the star.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Faster_With_Friends_TrainerRoad_Group_Workouts_7655.html b/site/slowtwitch.com/www/Indoor_Training/Faster_With_Friends_TrainerRoad_Group_Workouts_7655.html new file mode 100644 index 0000000..b41439f --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Faster_With_Friends_TrainerRoad_Group_Workouts_7655.html @@ -0,0 +1,654 @@ + + + + +Faster With Friends: TrainerRoad Group Workouts - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Faster With Friends: TrainerRoad Group Workouts

      + +
      +
      +
      +

      “Does this make people faster?”

      That is the first question the TrainerRoad team asks themselves when they consider new features for inclusion in the application. That simple philosophy is at the heart of what makes TrainerRoad continue to thrive in an increasingly crowded indoor training market. Since TrainerRoad’s introduction in 2011, the goal has always been to create an intuitive and easy-to-use tool that allows an athlete to focus on their cycling performance goals. There are certainly flashier or more entertaining products out there, but for the individual who is intrinsically motivated by seeing their own improvement, there are few that will do a better job of giving that rider the tools they need than TrainerRoad. If you want to get faster, you need to give TrainerRoad a look.

      Through the use of VirtualPower, adoption of the open ANT+ protocol to support a wide variety of sensors, a robust selection of workouts designed as part of a an equally impressive collection of training plans, useful metrics, and a simple user experience, TrainerRoad ushered in a new era in indoor cycling by bringing power-based workouts to the masses. They weren’t necessarily the first to do any of these things, but they put them together in a tidy package that made it easier and more affordable for more people to train with power. Where only the pointy end of the field had access to expensive, proprietary training tools before, TrainerRoad opened the doors to a much larger crowd.

      I had the pleasure of doing my first TrainerRoad group workout with TrainerRoad’s Communications Director, Jonathan Lee. With Jonathan in his garage in Reno, Nevada, and me in my fit studio in the suburbs of Indianapolis, Indiana, we discussed TrainerRoad’s philosophy, target audience, and deliberate approach to product improvement. Jonathan is passionate about cycling - racing MTBs, crits, or time trials just about every weekend. Spending an hour with him was fun, informative, motivating, and a really interesting look into TrainerRoad’s culture.

      The Philosophy of TrainerRoad

      TrainerRoad is built on three pillars to make people faster:
      • Planning
      • Training
      • Analytics
      These pillars were established in the beginning, and support every enhancement that has been introduced since. The product has always centered around the training plans, which started with a broad selection of plans covering a spectrum of cycling disciplines, training volumes, and athlete levels. Enhancements including the Calendar and Plan Builder support the athlete in the design and scheduling of their training and racing seasons. Analytics provide feedback to the rider, allowing for dynamic adjustment in search of PRs. TrainerRoad has dedicated teams who study the plans and workouts being used by athletes in over 150 countries, and work with their coaches to refine the plans and workouts, as needed. Everything in TrainerRoad is carefully considered and allowed to evolve in the pursuit of progress and PRs.

      This brings us to our main topic today: the new TrainerRoad group workouts. Why group workouts? Simple: Being able to ride with friends with common goals can be extremely powerful.

      Group workouts enhance the rider’s support system. The goal of the TrainerRoad group workouts is not to move towards the entertainment end of the spectrum and capture a new customer base, but to further enhance the experience for the existing TrainerRoad customer with a powerful motivational tool.

      Group workout support is something that the TrainerRoad team has wanted to do for a long time but, as with everything they do, they wouldn’t do it until they could do it right. The timing of the release couldn’t be better, considering the current global situation. The group workout functionality started as a proof of concept several months ago, and with a serious push earlier this year, was able to be implemented as what Jonathan described as a “minimum delightful product.” They have started simple, and will continue to grow the feature set as appropriate. Even being out in the wild only a few weeks now, it has already seen some enhancements.

      The current group workout functionality has the following features:
      • Anybody can create a group workout.
      • Up to 5 people can be in the group workout.
      • The person who starts the workout (which is likely the person who created it, but it may be whoever shows up first) is the host, and when they start the workout, they start it for everyone.
      • It is supported on Windows PCs and Macs only.
        For the best experience, a webcam, sound, and microphone are all supported.
      • A fast Internet connection is important, although it can run using a 4G hotspot.
      The ability to do a group ramp workout has already been added, along with the ability to display additional metrics. There are already big enhancements coming: the number of riders will soon increase from 5 to 11, and an upcoming update will allow for more control over your sound and video settings. The increase in the number of riders is enabled, in part, by changing the communications structure from a peer-to-peer approach to a server-based approach. This will not only allow for more riders in the group, but it will improve the throughput for better quality video and sound.

      +

      Setting up your first group workout

      Group workouts are pretty easy to do, overall. Here’s the process:

      +

      1. Log in to TrainerRoad on your Mac or PC.
      2. Select “Group Workouts” from the left menu.

      +

      3. In the “Create a Group Workout” section, select “Browse Workouts”.

      +

      4. Select the workout that you would like to do, and select “Create Group Workout” in the lower right corner.

      +

      5. A workout code will be generated. Copy this code and share it with your friends.

      +

      6. When it’s time to start your workout, return to the Group Workouts screen, and enter the workout code in the “Enter code…” field and select “Join”.
      7. While you are waiting for other riders to join, you can set up and test your webcam, microphone, and speakers.

      +

      When everybody has arrived, select “Start Workout”. As the host, you will start the workout for everybody.

      +

      So how was my First Group Workout Experience?

      Honestly, it was flawless. Jonathan had created the group workout, sent me the access code, and I connected just a few minutes before our scheduled workout time. We kicked off the workout and everything worked perfectly. The video quality wasn’t especially high definition, but it was adequate. Sound quality was good, and we never had any kind of drop, lag, or synchronization issues. Overall, for a “minimum enjoyable product”, my first TrainerRoad group workout made for maximum enjoyment.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Feelin_Rouvy_7020.html b/site/slowtwitch.com/www/Indoor_Training/Feelin_Rouvy_7020.html new file mode 100644 index 0000000..c2ca9ba --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Feelin_Rouvy_7020.html @@ -0,0 +1,646 @@ + + + + +Feelin' Rouvy - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Feelin' Rouvy

      + +
      +
      +
      +

      If I understand correctly, Rouvy is about to join Zwift and Tour de Giro and a short list of others by offering a multi-rider experience. Rouvy is well loved by its users because of its advanced metrics, trainer compatibility, features, and large library (thousands) of video routes. Rouvy (like Tacx) offers videos as opposed to graphics (Zwift).

      A lot of people like the notion of an actual video with elevation and topography that syncs with a smart trainer. Here’s a Rouvy experience (which I call by Roovy and others Rovey; maybe I'm wrong but I can't get out of my pronunciation habit).

      +

      While at Interbike, as I was walking from my last appointment to my next, I saw the video below and it caused me to be late for that appointment. What I saw was a video showing two riders on the same course, in the same video. Pardon if you’ve seen this before, I’m not the first person to report on it. But I hadn’t seen it in person until last week at Interbike.

      You guys are tougher than me. You stick to your schedule, get up in the morning at oh dark thirty, do your structured training workouts, get on with your lives. Me? I have needs. I have weaknesses. I need extra motivation. Company. Scenery. Sensory input.

      That’s why I like Zwift. Even the rides (as opposed to the races) are like snowflakes, or chess games: No two are alike. There’s always the injection of human decision and behavior – you can’t control how others ride, but they may impact how you ride. Some people prefer structured training precisely because of this – they don’t want the trajectory of their rides bent by others. Me, I’m just more likely to ride if there are people to ride with. That’s why I like multi-rider experiences.

      +

      +

      This was Interbike. A trade show. Lots to see. Not that much time to see it. So, I don’t have the whole 411 on Rouvy’s multi-player mode. But I took a video of the video (above), so, please bear this in mind if you click “play”. This was a super crisp video I was watching (as opposed to what you are watching. Like Kramer making his bootleg movies there’s a lot lost in the translation in this video-of-video. Still, I hope you can see what’s happening: Two riders on the screen at the same time, and “I” am getting passed and distanced by another rider.

      One limitation of Zwift is the incredible time and work it takes to come up with new routes. I’m in absolute awe of the graphics work they do. The Rouvy platform does not require that. In the case of this video above, riders enter and exit Canazei, a town at the base of a number of famous climbs in the Dolomites, annually ridden in the Tour of Italy. Who doesn’t want to be there, riding those roads? In this video, I can. Sort of. Of course, I could before this. But now I can ride them with friends. Or so it seems.

      Rouvy is slow-walking the reach of this functionality. It says it doesn’t want to be Zwift, or compete against Zwift. Okay.

      You and I will know more next month, if things remain on schedule. Rouvy is in private beta now on the multiplayer product, and intends to push its beta public in October. Rouvy is well aware of the indoor training ardency of Slowtwitchers. Look for opportunities as we move to a focus on stationary just after Kona. Here is what Rouvy says about its multiplayer experience.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Garmin_Buys_Tacx_7161.html b/site/slowtwitch.com/www/Indoor_Training/Garmin_Buys_Tacx_7161.html new file mode 100644 index 0000000..cb04776 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Garmin_Buys_Tacx_7161.html @@ -0,0 +1,644 @@ + + + + +Garmin Buys Tacx - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Garmin Buys Tacx

      + +
      +
      +
      +

      Tacx, the highly regarded maker of stationary training hardware and software, just announced its acquisition by the electronics giant Garmin.

      “Tacx brings an entirely new product category to Garmin’s fitness portfolio that expands our reach into the indoor training market,” said Cliff Pemble, Garmin president and CEO. “Together with Tacx, Garmin will offer a seamless, enjoyable and motivating indoor and outdoor experience for cyclists all year long.”

      “We are excited to have the support of a technology leader like Garmin,” said Koos Tacx, CEO of Tacx. “With Garmin’s extensive R&D capabilities and global distribution network, we look forward to working together to further enhance our indoor training products and technologies, and bring them to cyclists around the world.”

      +

      “The completion of this acquisition,” according to the release, “which is subject to customary conditions, is expected to occur in Q2 2019.“

      Each brand has problems to solve. Tacx probably the most highly regarded stationary trainer with its NEO. But, it is the most expensive, and it’s got compatibility issues – mostly with bikes, the inability to easily accommodate a number of today’s higher end bikes – but its software also trails Wahoo’s.

      “Meanwhile the release says that this acquisition, “expands [Garmin’s] reach into the indoor training market.” What reach is that? Garmin is a marquis brand that is shut out of the stationary market.

      The obvious brand in the background is Wahoo, orders of magnitude smaller than Garmin, yet, if Garmin’s Mr. Pemble were to lose even 5 minutes a night worth of sleep, Wahoo would be the brand interrupting his slumber. That brand owns a significant footprint both in stationary training and in outdoor information delivery (the head unit). Further, the market is waiting for Wahoo’s watch, and it’s not the watch, per se, it’s Wahoo’s capacity to easily integrate products into its ecosystem as well as its compatibility with bikes and with software service providers.

      Wahoo is rarely praised as having the very best product in a category, whether the Element or the Kickr. But Wahoo is frequently compared to Apple, in that once you buy something from Apple you’re likely to buy into the Apple ecosystem. This is Wahoo’s strength, and because Garmin doesn’t play in stationary the Kickr is just one more entry point into the Wahoo ecosystem (if you own a Kickr you’re much more likely to buy an Elemnt).

      Tacx is real, and it’s good. It’s just that, inside the U.S. – far the leader in stationary – Tacx is trying get the Hula Hoop to go round and round, with spotty success.

      “The combined expertise in the indoor training and outdoor cycling markets is highly complementary and will allow us to expand our product offerings and further serve our growing cycling customer base,” said Tacx spokesperson Marije de Gruijter. “We continue the Tacx brand and offer its industry-leading indoor bike trainers to cyclists around the world.”

      Distribution might be the biggest problem Tacx has. It’s suffered in two areas: It’s never been good at distribution in the United States. It hasn’t had its own U.S. headquarters; rather it’s relied on distributors. It hasn’t kept pace with Wahoo, a U.S. company enjoying an advantage in distribution in the most important country in which Tacx needs a bigger footprint. Further, Tacx has a terrific library of videos for use by its customers, but Tacx is losing that battle to Zwift, Peloton and even Rouvy, at least in the United States. Tacx just has not had a history in the United States to even remotely match its success elsewhere, even though its product clearly deserves a much bigger profile in the U.S., both its hardware and its software.

      But Garmin also has ease-of-use issues. Its software is also considered clunky. Here are two great hardware makers – Garmin and Tacx – joining forces. But neither has the flair for the ecosystem that Wahoo has. Wahoo can get the Hula Hoop to work. Can Garmin and Tacx figure it out?

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Gettin_Groovy_with_Rouvy_-_Part_1_Getting_Started_7099.html b/site/slowtwitch.com/www/Indoor_Training/Gettin_Groovy_with_Rouvy_-_Part_1_Getting_Started_7099.html new file mode 100644 index 0000000..85987c5 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Gettin_Groovy_with_Rouvy_-_Part_1_Getting_Started_7099.html @@ -0,0 +1,653 @@ + + + + +Gettin' Groovy with Rouvy - Part 1: Getting Started - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Gettin' Groovy with Rouvy - Part 1: Getting Started

      + +
      +
      +
      +

      More has been done in the past decade to make the pain cave more fun and effective than in the entire prior history of this segment of cycling. Stationary, and gravel, that's where the new action is in cycling. The days of cranking away on the trainer while watching Tour de France reruns are behind (most of) us. Last year I wrote a multipart series on Zwift, which is being reprised with updates on Slowtwitch now. We're going to marry this with how-to demos on other platforms, to help you find the solution that meets your needs. Today, we will begin our look at Rouvy.

      +

      What is Rouvy?

      Rouvy is a new name for a product that has been evolving for over 10 years already. Rouvy was previously known as VirtualTraining, developed in conjunction with CycleOps. About a year ago, VirtualTraining was renamed Rouvy and the product was released for hardware from other manufacturers including Wahoo, Elite, Tacx, Kinetic, BKOOL, and more. This is a trend - the separation of hardware from the software, with open communication protocols like ANT+ and Bluetooth allowing for the freedom to talk to and control hardware from many manufacturers.

      Rouvy is an interactive platform for structured workouts and more, providing data analytics to help you or your coach to track improvement. Like other applications such as Zwift, TrainerRoad, PerfPRO Studio and others, Rouvy's goal is to motivate you – to make it interesting. Rouvy is very visual, in the sense that it provides a way to ride different places virtually, like Zwift. The departure is that instead of being a game-like virtual world, Rouvy's main draw is video routes - thousands of GPS-synchronized videos of actual routes from around the world to give you the opportunity to explore our real world.

      When you ride a video route, the video is synchronized to your speed. If you are riding at 20mph but the video was recorded at 15 mph, the video framerate will adjust accordingly. The Rouvy video collection allows you to be riding in the Alps one day, and in Oregon the next. This might be the closest you can get to traveling the world without excess baggage fees.

      Rouvy is not unique in this aspect as Tacx, BKOOL and others also offer similar video routes. This is where Rouvy has recently introduced the Beta for their augmented reality, which will allow you to ride with others from around the world, somewhat similar to Zwift, but laid over real video.

      +

      How do I get started?

      If you've heard enough and want to give it a try, you first need to get your hardware in place. Rouvy is available for Windows, iPads, iPhones, and Android devices. As indoor training evolves, the demands on your system continue to increase, and the quality of your system will influence the experience. In particular with the Windows platform, you will get better frame rates, resolutions and smoother performance with a more powerful CPU, GPU, and memory. The requirements below can be considered the bare minimum:

      Windows
      • Windows 7, 8, or 10
      • 2.33 GHz or better x86-compatible processor, or 1.6 GHz or better Intel Atom notebook processor
      • 512 MB of RAM (1 GB recommended)
      • ANT+ connectivity (via a USB dongle such as this one) or Bluetooth (Windows 10 only)

      iPad
      • iPad 2/mini or newer with Bluetooth 4.0 built in

      iPhone
      • iPhone 4S or newer with Bluetooth 4.0 built in

      Android
      • Android 4.3 or newer with Bluetooth 4.0 built in

      By comparison, these system requirements are pretty light compared to Zwift, which requires 4 GB of RAM and a 1 GB GPU for a Windows system. Rouvy currently does not support MacOS, but development is in the works. You can also use Apple TV via your iPad or iPhone using AirPlay.

      What is the best option? If you like to keep it simple and your hardware supports it, Bluetooth with an iPad is really nice - you don't need a dongle and the iPad provides a nice-sized screen for viewing the video (especially if you can put it on a music stand or something similar to have it right in front of you as you ride).

      The other key hardware piece is your trainer. Like many other software tools, Rouvy supports virtual power, power meters, and smart trainers. The Rouvy indoor ride experience can be enhanced by the level of sophistication of your equipment. A wide variety of equipment is supported, so you are bound to find something in your budget. A supported “dumb” trainer with a speed sensor is enough to provide virtual power information. A power meter will increase the accuracy. A smart trainer can be controlled by Rouvy to provide resistance feedback based on the GPS information from the video route, increasing the realism. Here's a list of supported trainers.

      As we discussed in the first part of the Zwift series of articles, the communication hardware needs are dependent on the trainer, sensors, and the hardware you will be running Rouvy on. While ANT+ was the dominant sensor/trainer communication protocol up until a couple of years ago, Bluetooth is gaining more support. There are a few Bluetooth-only devices, such as the Kinetic Smart Control trainers, but many devices are making it easier by incorporating both ANT+ and Bluetooth.

      +

      Installation and Setup

      Once you have your hardware and bike set up, it is relatively easy to get started with Rouvy. Depending on your computer platform, installation is simply a matter of downloading the application, installing it, setting up your Rouvy user profile, and pairing your sensors. For the most part, this is relatively straightforward. The download will take some time, so you will want to plan for that, allowing yourself at least 30 minutes to get Rouvy downloaded and configured.

      Whenever you fire up Rouvy, you will be presented with your Dashboard, as shown in the image above. From here, you can adjust your settings, pair your sensors (the bar at the bottom of the screen), and select your ride.

      Before you ride for the first time, there are just a couple of things to do:

      1. Pair your sensors
      2. Set up your profile

      Pairing your sensors is a relatively straightforward process - put on your heart rate monitor if you have one, hop on the bike and start pedaling, and Rouvy will recognize any sensors in the area. The initial ride will involve just a bit of configuration. Once recognized, they will be saved for subsequent rides and you can just get on and go.

      +

      What are we waiting for? Let's ride!

      Rouvy provides rides in the form of video routes or power-based workouts, along with the ability to do a free ride, FTP test, online races, or challenges. You can select any of these options from the left menu on the dashboard. For now, we'll go to “Augmented Routes” and pick a video route.

      +

      Rouvy provides two different views of the route list; the “Details” view above gives you a small preview of the video, but the Table view below provides a bit more detail:

      +

      When you click on any of the routes, you can get some additional detail before you make your final decision on where you are going to be riding today:

      +

      The Italian Dolomites sound lovely this time of year, so that's where we're headed. Click the “Start Ride” button to get going! You will now see the “Route pre-training” screen, which gives you an opportunity to download the video or stream it, turn the augmented reality on or off, calibrate your trainer, and select virtual training/racing partners. For now, we'll just stream the video with the augmented reality on and ride solo. We will cover these functions in the next article.

      Finally, one last decision before you start riding - you can either race or train. We'll cover that in the next article, as well. For now, we'll just go for the training option. Click Start Training. The course and video will take a moment to load. You can warm up a bit or jump right into the course. If your screen looks like this, you're now getting groovy in Rouvy!

      +

      Once you are done with your ride, either by finishing the route, stopping pedaling, or clicking the Menu button near the upper right corner of the screen, you can see your ride statistics and power information. We'll cover all of that in more detail in a future article.

      What does this look like? Here's a short video:

      +

      +

      What's Next?

      Keep an eye on this space for more articles on Rouvy. We'll look at the route screen and controls in more detail, explore the structured workout functionality in greater detail, and look at the other ways Rouvy makes your indoor experience more fun!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Getting_Started_with_Ironman_VR_7652.html b/site/slowtwitch.com/www/Indoor_Training/Getting_Started_with_Ironman_VR_7652.html new file mode 100644 index 0000000..fd18d16 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Getting_Started_with_Ironman_VR_7652.html @@ -0,0 +1,650 @@ + + + + +Getting Started with Ironman VR - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Getting Started with Ironman VR

      + +
      +
      +
      +

      Stepping back to view the big picture, it’s fascinating to see how the COVID-19 pandemic is changing us - as a society and as individuals - for the better. Those who prepare for and face challenges will come out stronger in the end. Endurance sports are all about challenging ourselves, and many of us continue to pursue our fitness goals and adapt to our world of stay-at-home orders, self-isolation, closed gyms, and social distancing. With a bit of creativity, some have found ways to enhance their overall fitness by being forced to do things a bit differently. There are opportunities to have fun - if we look for them.

      For some of us, competition is the fun, and indoor technology has evolved enough to allow that to happen. Virtual races have been going on for a couple of years now to give us a chance to feed our competitive nature. You can look at the event lists on Zwift, Rouvy, or CVRcade and find several races on any given day. For many, these aren’t a replacement for the “real” thing, but for some, they can be a fun racing fix.

      Faced with a global pandemic, virtual racing is getting some extra attention - looking through some of the event services, you’ll see more virtual running races or similar events being offered where you run on your own, submit your time from your Garmin or similar device, and get a virtual or real reward sent upon completion. Some events that are being cancelled, such as the 500 Festival Mini Marathon here in Indianapolis, are offering virtual alternatives. Our community is doing the best we can with what we have, and it’s really not half bad - we can make our own fun.

      Enter IRONMAN: the brand synonymous with presenting difficult challenges to those who seek them. Faced with having several Ironman events around the globe cancelled or postponed, they have introduced IRONMAN VR, which is the virtual race event component of IRONMAN Virtual Club. At first glance, the website doesn’t give a lot of information unless you sign up. Let’s dig in and see what we can learn.

      +

      The IRONMAN Virtual Club

      The broader component that we need to understand first is the IRONMAN Virtual Club. This is the online Ironman web application that is described as the “go-to hub for all things IRONMAN.” Anybody can join and it’s free - you just need to register and create an account. As you create your account, you’ll walk through a few information panels that start to clarify what you’re getting into. It would be nice to have that information before registering, but this has come together rather quickly, and things like this can be forgiven. As a member of the Virtual Club, you can access IRONMAN+ articles on training, fitness, and nutrition, shop for IRONMAN gear, and participate in challenges and races, such as the IRONMAN VR series.

      The main view is your dashboard, where you can track your training, earn rewards, and access events. The events such as the VR races are all based on tracking your training, so the primary task you need to do once you’re registered is connect to your training tracking application(s) such as Garmin Connect, Under Armour’s MapMyRide/MapMyRun, Fitbit, Polar Flow, Nike+, and a few others, as shown in the screenshot below.

      +

      You may notice that one of the most popular tracking apps missing from the list is Strava. You can find more details about what happened there elsewhere, but for now, you can’t use Strava as your tracking app.

      One indoor training app that can connect directly to the Virtual Club is Rouvy. Rouvy was announced as the Official Virtual Cycling Training partner for IRONMAN, and as part of that partnership, Rouvy can directly import your training efforts to the IRONMAN Virtual Club. This doesn’t suggest that Rouvy is the only means to record your cycling efforts - you can use other applications such as Zwift or Peloton, as long as you connect those indoor training applications to a training tracking that is supported by both the training application and Virtual Club. Of course, you can also just go socially distance yourself outside and complete the sessions the old-fashioned way.

      The net result is that you track your runs, swims, rides, etc. like you normally do, regardless of whether they are indoor or outdoor. As long as you track your workouts, you will get credit for the work in IRONMAN Virtual Club. So, for example, if you like to run outdoors and track your runs using your Garmin watch, once you finish a run and upload the workout to Garmin Connect, that effort will be subsequently posted to the Virtual Club dashboard without any additional effort on your part. As you can see in the following screenshot, I did a run and 2 rides that were uploaded to Garmin Connect. These were actually done in Zwift, which I have connected to Garmin Connect via Zwift’s connection tool. I finish a workout and almost immediately see the results in Zwift, Garmin Connect, and IRONMAN Virtual Club at the same time.

      +

      The information regarding your workouts in Virtual Club here is minimal - you’re not going to see mile splits, graphs, or any deep analysis of your workout. You’ll simply see the time and distance logged, along with points and credits earned for your effort. This is one major value add of Virtual Club - you can get rewarded. In the screenshot above, I earned 27 credits and 136 points for my 3 workouts. Points are earned based on the distance traveled and are then converted to credits that can be redeemed for rewards. The rewards currently available are discounts towards goods offered by IRONMAN partners. For example, if you earn 1000 credits, you can use it to get $100 off a ROKA wetsuit. Earn 20000 credits, and that’s good for $2000 off an IRONMAN VR edition Ventum One bike.

      +

      Are you Ready to Race?

      Once you’re connected and logging your efforts, you are now ready to participate in challenges or virtual races. So far, there have been three virtual IRONMAN events - VR1, VR2, and VR3 - held over the last few weekends. VR4 is the next race on the calendar, and happens this coming weekend, April 24 through April 26. You can register from your dashboard. There is no cost and no qualification. The format of VR4 is a duathlon consisting of a 3km run, 40km ride, and 10km run. VR3 was a similar format, but half the distances. VR2 was the same format and distances as VR4. VR1 was considerably longer, consisting of a 5km run, 90km ride, and 21km run.

      If you’re the person that likes to plan ahead, this might be a bit frustrating - currently, there isn’t any indication of race distance of upcoming races, or when they are. If you want to treat this virtual race with all the respect and preparation you would for an outdoor event, it would be nice to know what’s coming up over the next few months. Is VR5 going to be as long as VR1? I expect this to improve soon, but until then, you’re going to be jumping into these events somewhat spontaneously.

      When race weekend comes, you have the weekend to finish your virtual race. You can do the legs of the event in any order and over any time during the weekend, starting after 6:00PM GMT on Friday and wrapping up before 11:59PM GMT on Sunday. Whether you are riding indoors on Rouvy, running outside, or running in Zwift, you just need to have your connections in place and do the minimum distance for each leg, or session. On that note - don’t go too far, either - you are allowed up to 1 extra km for your session.

      Once you are all done, your results will be posted to the leaderboard and you will earn a badge on your dashboard. Is it anything like having Mike Reilly announcing “ - YOU ARE AN IRONMAN!” and having a finisher’s medal draped around your neck as you head through the finish line? No. But can it satisfy your urge to compete? You’ll only know if you try.

      Get out there and do it!

      In summary, it’s a pretty simple process:
      • Create a free account at app.ironmanvirtualclub.com
      • Connect to your training tracking applications (Garmin Connect, MapMyRide, etc.)
      • Register for a race
      • Complete the sessions of the race during the given time period, tracking your efforts on your fitness watch or via your indoor training application
      • Buy race swag (yep, they have stuff available for each race)
      So, there you have it - that’s how you race a virtual IRONMAN event. If you would like to see it in action (or are really, really bored), check out the live stream of my attempt at VR4 on this coming Friday evening, April 24, at 6:00 PM EST. I’ll be doing all three sessions back to back on my treadmill and trainer set up in the Vector Cycle Works fit studio. Considering I haven't run a 10k in years, this might be rather frightening...

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Getting_to_Know_Slowtwitch_Indoors_7607.html b/site/slowtwitch.com/www/Indoor_Training/Getting_to_Know_Slowtwitch_Indoors_7607.html new file mode 100644 index 0000000..3501fbd --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Getting_to_Know_Slowtwitch_Indoors_7607.html @@ -0,0 +1,644 @@ + + + + +Getting to Know Slowtwitch Indoors - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Getting to Know Slowtwitch Indoors

      + +
      +
      +
      +

      You could say that Slowtwitch was an early evangelist of Zwift -- we've been talking about the platform and hosting weekly rides almost from the beginning. We really enjoy the social aspect of riding with your fellow communities, as apparently do plenty of you: it used to be on a given ride I'd see around 5,000 other people riding in Watopia. That number is more like 15,000 now.

      Yes, circumstances of the world are now dictating part of this change. We're now seeing shelter in place orders for many places in California. For many people, indoor training is going to be the only training for the foreseeable future.

      With that in mind, we wanted to re-introduce you to our four ride and two run events that we host weekly. We're also planning on ramping up the competitive activity, introducing at least one bike race and one run race per week, because with all of the race cancellations ongoing, we know that some of you are going to want to do nothing but race.

      +

      Slowtwitch Indoors Rides


      Slowtwitchers gather 4 times per week on Zwift: Tuesday morning's women's only
      Ride of the Valkyries, Tuesday Night's Dynamite, the Thursday AM Rise & Grind, and Thursday evening's Hilly Vanilli. Each of our rides has its own personality, but they all share the common trait of being open and inclusive to all ability levels.

      Both of our Tuesday rides are structured training workouts; this means that the group is banded together and will ride the "same" speed no matter the power output. The only ask out of you as a rider is to ensure that you keep the pedals turning over so that you don't fall more than 6 meters behind the group. Otherwise, no matter whether you're putting out 100 watts or 400 watts, you'll both be in the group. Structured training workouts are written most often by myself and Dan, although occasionally we'll slip a fast one by you with a guest workout from a coach or pro athlete.

      Our Thursday rides, however, are more like your local shop group ride that gets dubbed the "weekly World Championship." We do try to keep each group together for as long as possible. However, with Zwift removing the fence from group rides, we do have our fair share of flyers. Our ride leaders for these events do their best to keep the group controlled, and look to average the middle of their w/KG range by the end of it. Rise and Grind does usually go after sprints and KOMs after a warm-up period, and the final 10 minutes each week are countless attack after attack. With Hilly Vanilli, we try to keep everyone together for as long as possible, even when the group behind catches the one up the road.

      At the end of the day, we want everyone to have a fun experience. You can enhance that by joining the conversation on the Zwift Companion app. Our ride leaders tend to be quite chatty...except when I'm having to use my phone to mobile hotspot to run Zwift. I digress...

      Slowtwitch Indoors Runs


      We've pared back a little bit on our run schedule. Our Monday runs simply weren't getting the mass of people we were looking for. So, Wednesdays it is. Our two runs are pretty unstructured -- it's really about the collective showing up, on time, and running together on May Field. As we continue to build volume, we'll look to add a little bit more structure to these events with group workouts. My initial thought is that we'd do our weekly Zwift run race on Sundays -- let us know in the comments if you'd prefer another day of the week.

      We'll see you in Watopia.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Goodbye_Rouvy_Tuesday_7116.html b/site/slowtwitch.com/www/Indoor_Training/Goodbye_Rouvy_Tuesday_7116.html new file mode 100644 index 0000000..9540b73 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Goodbye_Rouvy_Tuesday_7116.html @@ -0,0 +1,657 @@ + + + + +Goodbye Rouvy Tuesday - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Goodbye Rouvy Tuesday

      + +
      +
      +
      +

      A fun feature of Rouvy is the ability to travel the world virtually, thanks to the routes based on real places around the planet. Whether you are just looking for some nice scenery or to pre-ride the course for an upcoming race, Rouvy has great options for you. In Part-1 of this series on Rouvy, we focused on getting you up and running in the most straightforward manner possible so you could do your first ride.

      +

      Now that you have had a chance to experience how Rouvy can enhance your indoor experience, let’s harness our inner Dora the Explorer and review Rouvy’s route functionality in depth. Let's take a deeper look at navigating Rouvy’s expansive collection of routes, get a better understanding of what we see on the screen during the ride, and take one more look at the data that Rouvy provides after our ride. We will focus on the Windows PC version today, but the functionality is similar on the other supported platforms.

      Navigating the Depths: Route Categories and Search

      +

      Rouvy has a wide-ranging selection of routes, which can be overwhelming and perhaps disorienting at first glance. As shown above, Rouvy provides several categories to help you find a route that suits your fancy. From the left menu, you can select “All Routes” and see the screen above. Note categories such as “Alps” or “Triathlon”. In total, the categories only show about 250 routes. Rouvy claims to offer thousands of routes, so where are the rest? The categories are nice to get a quick list of ride options, but if you want to see what options are really available, direct your gaze to the search tool on the right side of the screen.

      Let’s do a search for the 5-star rated routes that have video. On the right side of the screen, we can slide the CONTAIN VIDEO slider to the right (it will turn green), select a RATING of 5 stars, and then click the Search button. You will get something like this:

      +

      This is the table view, sorted by distance. At the top of the screen, you see a message indicating that Rouvy has limited our search to the first 100 routes. We don’t know how many routes actually met our requirements, so we need to be a bit more specific. We can narrow down the search based on parameters such as distance, grade, or country. Rouvy also provides a map view:

      +

      In the example above, we’ve zoomed into Hawaii (double click the map to zoom in, double right-click to zoom out) and see a few video routes. Clicking on any route will provide additional details:

      +

      When viewing the ride details, as shown above, you have the “View More” button available. This will take you to the Rouvy website for additional information, including a map, elevation profile, the source video, and more:

      +

      A ride in Hawaii in October sounds quite lovely...

      Getting back to the ride details, you will see options to save the route as a favorite or start downloading the video. Marking routes as favorites is a nice way to keep track of rides you find. Downloading is an appealing option if you have a slow connection and adequate available storage space. One note on downloading - this doesn’t act as a buffer like you might get on Internet video like YouTube, etc. If you start a download, finish the download before your ride. Rouvy will use the video file in its usable state in storage at the time of the ride start. If you only downloaded 20% of the video, you’ll be staring at a frozen screen 20% of the way into the ride. So, plan ahead if you download.

      You can access your downloaded routes from the left menu, under the “Downloaded” menu option. You manage your library of downloaded routes using the Download Manager, which can be accessed via the Downloaded screen or in Settings.

      +

      Head’s Up: Exploring the Video Route Screen

      We’ve searched the globe for our route, so let’s ride it! Once you’ve selected your route, you will navigate to the “Route pre-training” screen to finalize your options before your ride. You can think of this screen as your last chance to check your connections, calibrate your trainer, and set up your ride experience.

      +

      For now, we’re going to look at the video options presented here. We’ll worry about the Partner stuff in an upcoming article. Route Video allows you to turn the video on or off. If you do this, you will see the Google Maps view of the route.

      The other piece to note is the Augmented Route training. As indicated, this is in public beta at the time of this writing, and allows you to turn on the virtual riders and other visual effects provided by the augmented reality. You can turn it off if you don’t care to see it. Note that the overall view is going to be different. The difference is shown below, with the augmented view on the left and the regular training view on the right:

      +

      These are the default views with everything turned on. You can clean up either view, if you’d like.

      The last step before you ride is deciding if you want to train or race. For now, we’ll select the training mode, and will cover racing in an upcoming article.

      Once you start your ride, the display is relatively clean, with a handful of screen elements positioned as to not distract from the video:

      +

      You’ll see your metrics along with the altitude detail in the upper left corner, elapsed time at top center, and a few controls and the secondary view in the upper right.

      The route elevation profile is shown at the bottom of the screen, with an indicator of where you are on the route (the blue circle in the screenshot above). You can drag the indicator to anywhere on the route.

      +

      A new augmented reality feature of Rouvy is live interaction - if someone else is riding the same route you are, you will see an indicator of their position on the course. You can drag your indicator to their position and ride together. In the screenshot above, I had Mackinac Island in Michigan all to myself.

      The buttons in the upper right part of the screen provide additional control of your view. Clicking on the eye icon (I overlaid a red arrow on the screenshot 2 images above) will toggle between 3 modes - video mode, map view, or statistics view (which are represented just above).

      The rewind icon (next to the eye icon, the 2nd of 4 icons in a row on the upper right of the screen) is the lap marker - you can click this at any time to manually mark a lap. The pause button does as you might expect. The right-most icon is the menu, which will bring up the following screen:

      +

      Here, you can toggle the views with the 3 icons on the left, relocate on the course to another rider’s position to ride with them (if there is someone else riding), adjust your settings, or end your ride.

      +

      The settings button brings up another menu (above) allowing you to control the different elements of your ride experience.

      +

      You have several settings regarding the map view, including satellite, outdoor, or street mode, distance and angle, terrain on or off, etc. You can adjust the graphics quality, which will change the controls on the screen, not the video resolution. You can toggle the video smoothing, and also adjust your reality level. If you have a smart trainer, this is how realistic the resistance feedback will be based on the road grade, your speed, and body and bike weight.

      Finally, you can toggle some elements of the screen on or off. The screenshot above has the secondary view and altitude detail turned off.

      That's enough for today. More to come. Why are we taking this time with Rouvy? With all respect to and affection for gravel, stationary is the exploding market in cycling. What is the most highly-valued business in cycling? Trek? Specialized? Shimano? Giant? Arguably, it's none of those, rather it's Peloton. Soul Cycle is not far behind. Zwift is "our" version of Peloton, and it's a monster. As stationary skyrockets, so do the platforms, like The Sufferfest, Trainerroad, FulGaz, Golden Cheetah, Road Grand Tours, Tacx, BKOOL, PerfPro and a dozen more and another dozen coming. If you limit the important platforms to what you can name one hand we think Rouvy - both what it is and what it threatens to become - deserves to be one of the fingers.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Hand_Me_the_Remote_please_An_Introduction_to_the_Zwift_Companion_App_7503.html b/site/slowtwitch.com/www/Indoor_Training/Hand_Me_the_Remote_please_An_Introduction_to_the_Zwift_Companion_App_7503.html new file mode 100644 index 0000000..fbe802f --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Hand_Me_the_Remote_please_An_Introduction_to_the_Zwift_Companion_App_7503.html @@ -0,0 +1,655 @@ + + + + +Hand Me the Remote, please: An Introduction to the Zwift Companion App - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Hand Me the Remote, please: An Introduction to the Zwift Companion App

      + +
      +
      +
      +

      Zwift offers a lot of in-game features and controls that allow you to adjust your experience and interact with other riders nearby, but it might be a bit hard to reach for the keyboard or touch the screen out in front of you when you’re riding or running at full tilt. Zwift’s free Companion app might be just what you need - it’s your personal assistant and remote control for all things Watopian. Let’s have a look at Zwift Companion!

      +

      What is it?

      If you’re new to Zwift, the purpose of the Zwift Companion app might be a bit confusing. First off, it’s not the full Zwift platform - it’s more of an accessory application to enhance your Zwift experience by providing an alternate set of controls for how you interact with Zwift before, during, and after your ride. If you’ve been using Zwift without the Companion app, you’ll be familiar with the on-screen menu that pops up if you click your mouse button on the screen or touch your touchscreen device. Your game controls - menu options, camera controls, messaging, navigation, and other tools - are all moved from the computer on which you’re running Zwift into your hand by having the Zwift Companion app installed on a separate device such as your phone or a tablet.

      +

      You don’t have to have the Zwift Companion to enjoy Zwift, but it does offer several different ways to enhance your experience:

      +

      Communication

      Zwift Companion allows you to communicate with other Zwifters, even when you’re not riding. If you’re taking a break from a hectic work day, you can check in on your friends and see what they’re doing in Zwift. You can give them a Ride On! or send other words of encouragement by messaging them directly. If you’re riding as part of a group, you can message people in the group, directly message others, or send a general message to anybody nearby. Just like in the menu, you can wave at other riders and runners, or just let everybody around you know that you’re toast and it’s time to go back to the analog world.

      +

      Events

      Zwift Companion puts events at your fingertips. At any time, you can look for cycling or running events, and even filter based off the type of event you want to do. Looking for a race or a time trial to test your legs? You can filter by the type of ride and even the category so you can make sure you’re up against people on your level. Maybe you want to join in on a virtual Turkey Trot? You can filter by distance and event type and race off those Thanksgiving calories (or make room for more). Of course, if you want to join in on some of the SlowZwift weekly fun, you can look for those events through Zwift Companion, too. Zwift Companion really makes for a nice way to stay on top of all of the action in the Zwift worlds.

      +

      Extra Features

      The Companion app also provides some features that you don’t normally get. Meetups allow you to create or join informal group events with your friends. A new steering feature allows you to pilot a mountain bike through some new trails hidden away in Watopia. We’re going to be covering these features in separate articles in the near future.

      +

      Enhanced Connectivity

      One really neat feature of the Zwift Companion is that it allows for Bluetooth connectivity for your sensors, providing an alternate means of pairing Bluetooth-capable heartrate monitors, power meters, smart trainers, speed sensors, or cadence sensors. This is especially handy for AppleTV users, as the Apple TV only allows for a couple of sensors to be connected via Bluetooth, plus the AppleTV remote. You can connect your sensors via the Zwift Companion app and overcome that limitation.

      +

      How do I get it?

      If all of this sounds good, the Zwift Companion app is easy to acquire. It works on iOS or Android devices, so you can just search for it in either the Apple Store or Google Play, depending on your selected platform, and download the free app. You will need to run it on a device separately from where you run Zwift. So, if all you have is an iPhone, it doesn’t really make any sense to run Zwift Companion, as you would have to try to toggle back and forth between the apps. That won’t work well. You need to think of it as a remote control for your main Zwift device, whether that is another phone, an Apple TV, a PC, or an iPad or other supported Android tablet.

      +

      Once you’ve downloaded and installed the Zwift Companion app on your device, you can start it like you do any other app. Make sure you are connected to the same WiFi network as your main Zwift device. When you first open the app, you will be prompted to log in with your Zwift credentials. You will start Zwift like you normally do on your main Zwift device, and once you start riding, you can take control with the Companion app.

      +

      That was easy!

      And there you have it - a quick start with Zwift Companion! As mentioned earlier, we’ll explore some of the new features that the Zwift Companion app enables in upcoming articles. In the meantime, have fun with Zwift! Ride on!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/How_Does_the_Latest_KICKR_Compare__6750.html b/site/slowtwitch.com/www/Indoor_Training/How_Does_the_Latest_KICKR_Compare__6750.html new file mode 100644 index 0000000..af9d74a --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/How_Does_the_Latest_KICKR_Compare__6750.html @@ -0,0 +1,618 @@ + + + + +How Does the Latest KICKR Compare? - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      How Does the Latest KICKR Compare?

      + +
      +
      +
      +

      It's February, and it's cold. Except indoor, where the riding is heating up! Slowtwitch just announced its second weekly Zwift event, the Hilly Vanilli. More questions are showing up on our Reader Forum: Which trainer to buy?

      Wahoo first released its popular direct drive smart trainer in 2012, and the KICKR is now on its 3rd generation. This seemed a good time to talk about the updates, and how well or poorly it fares against other smart trainers.

      +

      This latest iteration has only a few differences from the 2016 model, but it has come a long way since the original KICKR was overviewed here. I spent a few months aboard the 2017 model and, unsurprisingly, it remains a class leader in the smart trainer market.

      Technical Specifications

      Like most of the top tier smart trainers on the market today, the KICKR has a claimed power accuracy of +/- 2%, a max power output of 2000 Watts, and climb gradient of 20 percent. It supports Ant+, Ant+-FE-C, and Bluetooth Smart connectivity.

      +

      It also is compatible with 8, 9, and 10 speed Shimano/SRAM drivetrains along with 11 speed Campagnolo/Shimano/SRAM drivetrains. The 2017 KICKR is compatible with a bunch of hubs, including the 12x142mm and 12x148mm thru axle along with its KICKR CLIMB. The added compatibility for thru axles and the KICKR CLIMB are one of the updates to the 2017 KICKR.

      +

      All of the KICKR models provide speed, power, and distance information along with cadence when used with the included RPM cadence pod. Its frame has been time tested as durable, proven by the number of first generation KICKRs still operational today, including mine. The 2016 and 2017 KICKR models also include a handle for easier transport and connectivity LED lights found on the rear of the metal frame that confirm connection to peripheral devices (e.g., Ant+ via a flashing red; BTLE via solid blue).

      Finally, the footprint of the KICKR hasn't changed since its inception, measuring 21" x 28" with legs open and 21" x 9" wide making it one of the most compact when folded and widest when open, but not necessarily the most stable and I'll discuss that later.

      Competitors

      The KICKR 2017 has to compete against itself, in that it has to provide a convincing value narrative to owners of a perfectly functional prior edition KICKR. It also must overcome the value offered by stalwarts such as the CycleOps HAMMER, Tacx NEO, and Elite's Direto and Drivo.

      The 2017 KICKR, along with its enhanced capacity to pair with thru axle bikes (think about your future bike purchases!) is also quieter, has an attached handle, improved power accuracy, improved responsiveness and feel, and LED connection lights.

      Getting Started: How do I use it?

      Like most smart trainers today, the KICKR is as close to plug-and-play as there is, and there haven't been any set-up changes since the original model. The KICKR does not require a riser block or cassette (unlike the NEO and Elite models), assuming you're riding a 700c wheel with standard road tires. For most standard road or tri bikes, it is just a matter of releasing the stabilizing legs by pushing the blue button where each leg attaches to the KICKR body, connecting the bike to the rear dropout, and tightening the QR skewer.

      Those with disc brakes, a thru axle, or 10-speed will have to change the included adapters or add spacers which are all included in the box. Those not using the typical road/tri bike tires (700C) also have the option of adjusting the height for from 24-inch road tires up to 29-inch mountain bike tires. Finally, each stabilizing leg has a turn-adjustable foot-pod to ensure the bike is level with floor.

      Once this is completed, it is simply a matter of plugging in the KICKR to a power source, pairing the KICKR to either a Wahoo ELEMNT bike computer (or most newer Garmin Edge bike computers) or the various mobile applications or indoor software platform (e.g. TrainerRoad, Zwift, Rouvy, etc.), perform an initial spindown calibration and begin riding.

      Let's Take Zwift, As One Example

      You can load Zwift's software onto an iOS device, Windows 7 or higher, or an OSX computer (10.8 or better). If you enable Bluetooth in your OSX (Mac) computer or on your iOS phone or tablet, then you're set: The computer and the KICKR will automatically pair. Windows does not support bluetooth pairing; you must use an ANT+ dongle on Wndow's machines.

      If you don't have Bluetooth capability in your computer, no worries. What you need is an ANT+ dongle, these are tiny and they work. Here is one for $16.99 that our publisher successfully uses for his weekly Slowtwitch Zwift rides, use a USB extension cable if you need the dongle closer to the device. If you want to be all-Wahoo, this company makes its own USB Ant+ kit, with the dongle and the extension.

      If you have both wireless protocols enabled (an ANT+ dongle in your USB port and you have Bluetooth enabled), you make the choice on OSX. When you get to Zwift's pairing screen, and you click on "power source" (or controllable trainer), your KICKR will show up twice, once with the Bluetooth icon and once with the ANT icon. You can pick how you want to connect.

      Power, Sound, and Performance Testing

      In order to be consistent in testing indoor trainers, I have a few dedicated workouts for trainers that focus on some common interval durations (e.g., 10 seconds, 30 seconds, and 2, 5, 10, 20, 30 minutes). I also test across some common power ranges (200, 250, 300 watts) and then toss in some micro intervals or easy efforts (100 watts) where variations are more noticeable. Also, I repeat sound testing of each trainer to reduce the effect of ambient noise or drivetrain noise that could vary between tests.

      Power accuracy and sound were one of the areas where the second and third generation KICKRs were reported to have improved over the first-generation model, and testing confirmed exactly that. The accuracy was impressive when gauged to the Garmin Vector 3s and Pioneer dual-leg power meter.

      Sound

      I tested the noise made by the KICKR 2017 using the SPLnFFT mobile application placed on the non-drive side of the KICKR. I repeat the same test conditions for each trainer by riding at approximately 200 watts at a cadence of 75-80rpms on a freshly lubed drivetrain with a straight chainline. My testing had the KICKR 2017 very slightly louder (less than 2db difference) than the Tacx NEO, making it the second quietist smart trainer I have tested. I also conducted sound tests at higher power (400+ watts) and the sound deviation remained consistent between the NEO and KICKR, although at higher wattages the subjective perception is the KICKR was louder, which may be a function of tone differences rather that sound output.

      +

      Power

      Wahoo thought outside the box when it modified its power algorithms for the 2017 KICKR. It eliminated the use of strain gauges and in essence doesn't have what most consider a "real" power meter, unlike its first-generation model. The 2016 and 2017 models derive power primarily from the amount of brake resistance applied, along with increasing the number of environmental sensors.

      +

      The result, according to Wahoo, was improved power accuracy, fewer calibration issues, and improved durability. In fact, although Wahoo claims the power accuracy to be +/- 2%, they state those numbers apply to outlier wattages such as fewer than 100 watts and more than 800 watts. Sweet spot wattages will be closer, they say and my testing demonstrated that, as can be seen in the images above and below, indicating a power accuracy on par with the Tacx NEO and Elite Direto.

      +

      Performance

      Performance, or feel, can be difficult to objectively quantify when comparing trainers with similar technical specifications. There is some performance convergence among the models in the $1000+ category of smart trainers, as the important features are now identified and the companies have shorn up their weaknesses. Differences are often subjective and minute, as was the case with KICKR 2017.

      +

      The KICKR was on par, in road feel, with its competitors, with the only exception being downhill rides on ZWIFT where the Tacx NEO does a better job of simulating downhill grades. Also, the NEO's "road feel" feature is found only on the NEO. The upcoming release of the KICKR CLIMB may neutralize this difference. The above photo indicates the amount of vibration emitting from the KICKR at 200 watts and 80 rpms tested via a mobile application, the device placed directly on the KICKR. As can be seen above, the vibration is minimal, and shouldn't be an issue for neighbors who live below your pain cave.

      +

      During my testing, there were a few detectable performance differences I noted between the KICKR and Tacx NEO, some that arise from dropouts or power spikes. The NEO occasionally has resistance lock, or significant resistance increase, for no apparent reason and suddenly a 100-watt recovery interval will spike in power, as can be seen in the second interval above (where the resistance increased prior to the second 400-watt interval). This is not an isolated incident with the NEO and is something I have not experienced with the KICKR. I have seen the same complaint from other Tacx NEO owners online. Also, as can be observed in the first 4 intervals done on the NEO, the first few seconds of an interval the NEO overshoots the target power and then smooths out. This was also validated from power readings from the Vectors and Pioneer power meters. The KICKR 2017 is a bit slower to hit target power but much smoother in doing so than the NEO. However, this also means the KICKR 2017 has difficulty hitting target power, or measuring power, in micro intervals (fewer than 5 seconds) when compared to the NEO or a standard power meter.

      +

      Finally, although the footprint and weight dimensions of the NEO and the KICKR are similar, the design of the NEO makes it more stable at higher power outputs (1000 Watts+) or if a rider gets out of the saddle and rocks the bike. I believe this is a function of the NEO's design where the platform style legs of the NEO balance it on the floor whereas the KICKR relies only on the small adjustable foot pods (shown in above photo), with the trade-off being increased stability (NEO) versus ability to adjust for uneven surfaces (KICKR).

      Software

      Unlike most of the other smart trainer companies, Wahoo doesn't have software specifically for use with the KICKR. Why would it? Wahoo's apparent wager that software companies would fill that void has paid off.

      +

      Wahoo does have a very basic mobile application, Wahoo Fitness, that would allow a user to control level, resistance, target watts, and slope, along with allowing for software updates for the KICKR, but it is rather rudimentary compared to other applications. It does allow for power matching with an external power meter via "Control w/Ant+ Power Meter". In fact, the ELEMNT line of bike computers and most training platforms allow for calibration and resistance adjustment, rendering the mobile application redundant.

      Practical Considerations

      The flawless and simple integration between the KICKR and Wahoo's bike computer line is definitely something to consider when purchasing either a KICKR or the ELEMENT bike computers. Its ability to control the KICKR for resistance-controlled workouts, pull structured workouts from various sites (e.g. TrainingPeaks, Today's Plan) or .GPX routes for route simulations are all features I use daily.

      A more difficult consideration is whether one should upgrade from Wahoo's version 1 or 2 of the KICKR to the 2017 model. The improved sound, accuracy, and feel between their first model and the 2017 KICKR is substantial enough to consider upgrading However, unless one foresees purchasing the KICKR CLIMB, there really isn't a strong argument to upgrade from the second generation KICKR. Further, until I test the KICKR CLIMB I can offer no guidance on it. That established, Slowtwitch should be testing the product shortly and Wahoo has confirmed that public release is now set for April 2018.

      Final Thoughts

      Wahoo has almost closed the gap between the KICKR and the Tacx NEO, heretofore arguably the top smart trainer (and priced accordingly) in terms of power accuracy, feel, sound and compatibility. Among similarly priced competitors (such as CycleOps' formidable HAMMER), the KICKR is a formidable competitor. Wahoo clearly improved upon its original model, and the remaining question is whether the integration with the KICKR CLIMB will push the KICKR 2017 to the front of the smart trainer market.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/How_a_Woodway_treadmill_became_a_reality_6737.html b/site/slowtwitch.com/www/Indoor_Training/How_a_Woodway_treadmill_became_a_reality_6737.html new file mode 100644 index 0000000..6c98caf --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/How_a_Woodway_treadmill_became_a_reality_6737.html @@ -0,0 +1,659 @@ + + + + +How a Woodway treadmill became a reality - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      How a Woodway treadmill became a reality

      + +
      +
      +
      +

      I have been lusting for a Woodway treadmill ever since I experienced one of them at the NY Giants training center when I was there in 2011 to cover a Timex Triathlon Team camp, but the $12,000 price tag was a massive hurdle to me. A hurdle though that many others had apparently overcome by either buying it new anyway or hunting down a used version of this high-end and beastly Wisconsin made treadmill. Back then there was also not the right space in our home for any treadmill, but when we built our new home in 2015/2016 we had more space and also a room that was going to be a dedicated pain cave. I thus started to look more seriously at getting my hands on one, but also researched other more affordable treadmills. I knew that having a treadmill at home would be a game changer for me as I at times can't leave the house because I am the only parent at home. My wife's work schedule is very demanding and with a treadmill I could run when the boys are tucked in for the night, or even during the day while they play nearby. Plus of course with a treadmill you can really control your pace and a treadmill is weather independent. Theoretically any treadmill would serve those purposes, but for some reason I kept coming back to the Woodway and my awesome wife swiftly gave me the green light to go ahead. The folks who had bought new Woodway units all seemed completely happy with that investment and spoke very highly of their experience. But those people who bought them used also seemed quite content and good ambassadors for this Waukesha, Wisconsin brand.

      In September of 2017 I finally made the decision to purchase a used remanufactured Woodway Desmo Evo from Fitness Superstore in Benicia, CA and was really sold by the very attentive owner of the store and the great communication by the employees. Plus I liked the video that showed their remanufacturing process. This Desmo Evo set me back $4,999, plus I bought the 3 year parts and labor warranty for $499. Add on the $649 shipping cost to the room of choice and I was totally out $6,147. I could have added a new belt (something that is actually included by them for all other treadmills) for another $1,500, but I wanted the money bleeding to stop and thus did not add the new belt.

      +

      + +

      + +

      The unit shipped on October 6th and arrived at my house on October 25th. The delivery company that delivered the unit had never before delivered a treadmill and two guys seemed a bit unprepared for the job. They were perfectly friendly and working hard, but struggled to put the unit together. At one point I considered asking them to leave and finish the job myself, but I had wanted that type of delivery to avoid any problems in case the unit did not work properly, and thus the finger could not be pointed at me. I let them finish the job but could not test it at that moment as I had to run to pick up my kids. When I returned home and turned on the unit the display stayed blank, but the treadmill was surely on. I was frazzled and called the Fitness Superstore and one of their tech guys walked me through the setup and it turned out that the delivery guys never connected the display to the treadmill itself, and with that handled all systems were good to go. I enjoyed running on the treadmill but a few weeks later I got another surprise when the arms that hold the display moved a few inches when I held on to it. Surely my 185 pounds could not have overwhelmed this beast of treadmill, plus I had seen 300 pound football players on Woodways in the Meadowlands. I again called the company and they were horrified. It clearly was a mistake made by that local delivery company, and the Fitness Superstore sent out some replacement brackets overnight and contacted a local technician to handle this properly. When the crew from Advantage Fitness Products showed up they quickly replaced the brackets and set it up properly and since then it has been smooth sailing and lots of running on this Woodway Desmo Evo. In mid December I signed up for the Slowtwitch 100 runs in 100 days challenge and on day 60 I am currently at 61 runs. The majority of these runs have been on the treadmill and connected to Zwift. But that is via my Stryd foot pod, the treadmill is not Bluetooth or Ant+ connected.

      +

      + +

      +

      The treadmill however feels awesome and responds very fast to speed input and elevation adjustments. Many friends have come over to check out the Woodway and I think I actually could rent it out. I don't mind having a friend come over to run on it, but I will surely not make this a business. I am very pleased that I made this purchase and would absolutely go that direction again. Despite the early troubles I would also recommend the Fitness Superstore. Plus I actually got a $250 refund for the poor delivery service. Just be sure that a solid technician puts it together. Plus it actually really isn't wizardry, so you could do it yourself. Basically not much comes apart for the transport, but it is super heavy.

      +

      + +

      +

      This is the view from behind looking at the TV screen where the Zwift app is running. My Wahoo Kickr is sitting right in front of the treadmill, with maybe 5 feet of space between them.

      +

      + +

      +

      The display screen is very basic, but has the all the info I need or want. And typically my eyes are focused on the TV set where I watch my Zwift data.

      +

      + +

      +

      There also control buttons on the arms of the Woodway. Speed on the right hand side, and elevation on the other.

      +

      + +

      +

      This Woodway Desmo Evo unit of mine goes up to a 15 percent incline but does not have a decline feature. That though can be had.

      +

      + +

      +

      Up close with the unique treads of the Woodway. They are clearly worn but that has not hindered me in any way. And this running track feels more like being outside and is very different from any other treadmill I have tried before. It is hard to describe in words and really is something that needs to be personally experienced.

      +

      + +

      +

      Top speed possible is 12.5 mph and I doubt that I ever will find that limiting. That by the way is 4:48 pace which would get you a 14:53 5k.

      +

      + +

      +

      The badge reveals 2007 as the manufacturing year and shows the required electricity support.

      +

      + +

      +

      We have modern Legrand outlets and light switches everywhere, but not here. The Woodway requires a 110V NEMA 5-20R outlet with a dedicated 20 amp line.

      +

      + +

      +

      The treadmill is pretty loud, but does not really bother me. And I am not really sure how the noise compares to other treadmills, plus I think that this tall room amplifies the noise. I love the treadmill though and with the help of 2 Honeywell fans the sweating is kept in check.

      +

      All images © Herbert Krabel

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/It_s_Game_On_With_Zwift_6660.html b/site/slowtwitch.com/www/Indoor_Training/It_s_Game_On_With_Zwift_6660.html new file mode 100644 index 0000000..eb110bb --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/It_s_Game_On_With_Zwift_6660.html @@ -0,0 +1,653 @@ + + + + +It's Game On With Zwift - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      It's Game On With Zwift

      + +
      +
      +
      +

      [Note: This article was originally published in Autumn, 2017, and has been updated to reflect changes over the past 12 months. This is the 3rd of what will be 8ish articles on how to Zwift, the whole series binge-published over the next 2 weeks.]

      If you’re coming to this cold, this is the third installment in “How to Zwift.” We began by describing the easiest way to Zwift, requiring a very small cost outlay in tech and equipment. Then we dove into the experience. We took a high quality tool out of its box and learned to use it. That, by itself, has been fun. Now it’s time to craft something with that tool.

      What do you want to build? Zwift is a game, if that’s what you want it to be. Or a training tool. It’s also a virtual community (not as in Westworld, with real-looking people who nevertheless aren’t real; rather there’s real people behind the avatars in a group ride).

      +

      Just know that however you want Zwift to manifest itself – whatever experience you want to craft with this powerful tool – you still don’t need to “upgrade” your equipment. You can (if you want), but one luxury of Zwift is not needing to spend a lot of money on technical hardware.

      I’ll be what I am, a solitary man…

      For some of us, training time is alone time. Cycling is internal, not social. In my own Zwift experiences I’ve had a lot of fun just chasing whatever is in front of me – exploring the courses and routes, chasing the next turn, cresting the next hill, and going just a little bit farther than I thought I could. For some, that’s enough. But Zwift offers more.

      It’s On Like Donkey Kong: Experience Points (XP) and Rider Levels

      Not unlike the classic arcade hits, Zwift offers a means of building up your rider using a points system. There are 25 rider levels in Zwift, and you move up levels by accumulating Experience Points (XP). You earn points primarily by getting time in the saddle and putting in the miles (or kilometers), and you can see your progress towards the next level with the orange bar near the center top of the screen. Each mile earns you 30 points, and each kilometer earns you 20 points.

      Everybody starts out at level 1 and you must earn 1,000 points to get to level 2. It isn’t a linear scale, though - while it only takes 1,000 points (just over 33.3 miles or 50 km) per level to advance through levels 1-6, it takes 2,000 points to get to level 7, and then escalates rather quickly from there. If you want to get to level 25, you’re going to have to earn 110,000 points - the equivalent of 3,666.67 miles (5,500 km).

      But it’s not just miles ridden that hikes you to another level. You can expedite your progress across levels with Power-Ups, which I’ll discuss in a minute.

      +

      Kits, Wheels, and New Bikes

      You unlock some new features every time you move up a rider level - new kits, new wheels, new bikes, and new routes. For example, at level 2, you unlock a new jersey, getting to level 5 will net you a Specialized Allez, and level 10 is good for Zipp 808s. If you get to level 24, you earn a Cervelo S5.

      Yes, the new bikes and wheels will make you faster, as their weight and aerodynamics are factored into the game engine. So, they aren’t just for looks.

      You don’t have to ride for months to earn new features. There are a lot of kits available through promo codes. A quick Google search will reveal a bunch of options. In the last article, we gave you the SLOWTWITCH promo code that unlocks the Slowtwitch kit. Just type P to bring up the promo code dialog, enter SLOWTWITCH and press Enter. You have now reached the pinnacle of virtual cycling fashion.

      +

      Timed Segments

      On every route in Zwift, there are opportunities for some bragging rights - King/Queen of the Mountain (KOM/QOM) for climbs, sprints, and overall route/lap times. When you hit the start of a timed segment, the HUD will display a list of the standings on the left side of the screen, and your ETA will be displayed at the top of the screen. The standings are always moving - they are based on who is on course now and how long ago they achieved their place. You are basically racing whoever is currently on course and yourself. You may find that fast lap time you put down out of the gate may go away if you keep riding long enough.

      +

      Power-Ups

      We talked briefly about Power-Ups in the last article as a part of the Zwift HUD (Head’s Up Display) and how you can use them. The rider looking to Zwift as a training tool and ride simulator might find Power-Ups to be a bit too game-like or even see them as a form of cheating. They are frowned upon in some group rides and races. That’s where we have to accept that Zwift is a game that varies slightly from reality. If you can’t quite get that KOM from the guy who used the “lightweight” power up, that’s just part of the game.

      There are 5 different power-ups:

      Small Bonus - 10 extra experience points, independent of any mileage you’ve logged.

      Large Bonus - 250 extra experience points.

      Lightweight - indicated by a feather icon, this will reduce your weight by 15 pounds for 15 seconds.

      Draft boost - indicated by a van icon, this will increase the draft effect you are experiencing by 50% for 30 seconds. This really only helps when you are drafting off another rider.

      Aero boost - indicated by an aero helmet icon, this reduces your CdA by 25% for 30 seconds.

      So how do you get them? Power-Ups are somewhat random - you may get one by completing a timed segment and going through a finish arch. You can’t bank them up - you are only able to hold one at a time.

      +

      Pizza Power

      Sometimes the simplest motivator is food. Zwift has you covered here, too - your Pizza Power is the number of slices of pizza you earned during your ride. I wouldn’t recommend eating your pizza as you earn it - you’ll get grease on your keyboard.

      +

      Achievements

      Zwift provides a cornucopia of fun, odd challenges simply called Achievements. Some are earned by using features of Zwift, some are earned by putting in mileage, and a few are labeled as “extra credit.” Did 5 laps of the Watopia volcano route? That’s an achievement called “warmed up.” Hit 30 mph? You earn the “Whoa Nelly.” Watch someone else ride for too long? You earned the appropriately named “Stalker” achievement. If you can call that an achievement. Some are pretty easy to obtain, others take some tricks. Getting the Daredevil achievement by hitting 50 mph takes a little bit of learning.

      +

      Challenges

      If you are one who is motivated by something a bit more big picture, you can take on a challenge. Challenges are selected from the Pause or Start screen and you can only have one challenge selected at a time. You can switch between them, but will only be credited towards one challenge at a time. Currently there are 3 challenges:

      Climb Mt. Everest - 29,028 feet of climbing. All in a day’s work, right?

      Ride California - Cover a total of 797 miles to complete the Ride California challenge.

      Tour Italy - This one doesn’t get unlocked until you reach level 10, but once you do, you can ride 1,242 miles to cover Italy.

      None of these involve special courses - you don’t get a California course for participating in the Ride California challenge. You just have to use your imagination for now. You can certainly use some strategy for achieving these. If you’re looking to complete Everest, find a steep grade and do hill repeats. Trying to maximize your miles for Ride California? Stay on the flats and keep the hammer down. As you learn the courses, you’ll know where to find places to maximize your progress.

      +

      Goals

      If you’re the type of person who likes to set a monthly or weekly distance goal to reward yourself for consistency, Zwift has an option to set goals. On the start screen, just below the course calendar, you’ll find a button where you can set your goal in distance or hours and track your progress. In the image above, I set a goal of 5 miles per month. Hey, don’t judge me. Baby steps.

      +

      Missions

      Sometimes, sponsors will offer missions, providing yet another means to inspire you to get on your bike regularly. Any current mission will be displayed on the Start screen, on the left side. You may get a chance to win real prizes!

      +

      OK, this riding by myself stuff is boring. I’m a people person!

      Remember that Solitary Man bit? What about those who enjoy the social aspect of riding a bike? Besides the best-in-class graphics, and everything described thus far, the community element places Zwift in company among indoor training software products. Along with tools that display your data the interactive, game-like experience provides a sense of community by default - you see other riders out riding. You can draft off them, wave to them, send group text messages, or even give a “Ride on!” to another rider. It’s not just the impression you’re not alone. You’re really not alone!

      Zwift not only provides for this casual contact, with a bit of planning you can take part in pre-planned group rides or workouts. But this is enough to absorb! Virtual group riding comes next.

      Here's the whole series:

      1. Welcome to Watopia: Install Zwift using virtual power, the most cost-effective option.
      2. Zwift, The Deeper Dive: One more layer below the surface.
      3. It's Game On With Zwift: Zwift is a game, but also a virtual community.
      4. Zwift: Let's Dance! The social aspect; Zwift is a Massive Multiplayer Game.
      5. Zwift: A Potent Tool for Training: Zwift’s workout mode.
      6. On Zwift Every Day is Race Day: Zwift's racing culture and community.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/It_s_Hot_Sunny_Great_Riding_Outdoors._Translation_How_Do_I_Zwift__7350.html b/site/slowtwitch.com/www/Indoor_Training/It_s_Hot_Sunny_Great_Riding_Outdoors._Translation_How_Do_I_Zwift__7350.html new file mode 100644 index 0000000..6e0b15e --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/It_s_Hot_Sunny_Great_Riding_Outdoors._Translation_How_Do_I_Zwift__7350.html @@ -0,0 +1,652 @@ + + + + +It's Hot, Sunny, Great Riding Outdoors. Translation: How Do I Zwift? - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      It's Hot, Sunny, Great Riding Outdoors. Translation: How Do I Zwift?

      + +
      +
      +
      +

      Late to the Zwift Party? No matter. Let’s get you up to speed right now. We've published a lot of how-tos on Zwift, Rouvy, The Sufferfest, TrainerRoad. Some of these tools can be integrated into “Community experiences” and these are important to my own strategy here on Slowtwitch (I’ll get to that further below).

      Today, I’m going to recast our primers on Zwift, because I get the sense that a number of you are interested in joining but you’re sort of lost on where to start.

      Understandable! Zwift is like Dungeons & Dragons: Its functions, moving parts, hidden rooms and lonely paths are so numerous, and are added at such a rate, I question whether most joining today would ever fully know the place. It’s like an amusement park that adds rides faster than you can ride them all. Which I like! Because I’m easily bored.

      +

      But joining today is no more complicated than it was 2 or 3 years ago. In fact, less so. Less complicated and less costly. But first, why do I care? Fair question, and when I’m done with the task at hand I’ll be uncommonly transparent about my motives. Now, for those who’re new to Zwifting…

      You need 4 things to Zwift: A reasonable internet connection (most folks have that); a stationary bike trainer; a way to transmit a “performance” signal to your device on which Zwift (the Game) is loaded; and then you need that hardware, whatever it is. The hardware is a computer of some sort (even if just a handheld phone); and then a viewer, again even just a phone.

      +

      By far, in my experience, the easiest, cheapest, slickest way to Zwift is by using an AppleTV as your “computer”. An AppleTV isn't a TV. It's a little black box.

      You can use your iPhone or Android phone instead, an iOS or Android tablet, or a laptop or desktop computer. I’ve used pretty much all forms of Zwifting. The AppleTV is the slickest and cheapest, and the phone or tablet is least desirable because it's handy to use your handheld device to work as a “remote control” when you’re in The Game (as Zwift calls it). There’s a Companion App for Zwift, which you don’t exactly need, but it makes life easier (if you want to text message in The Game, or switch back and forth between modes, and so forth).

      When I lead Zwift workouts, I use 2 computers – the AppleTV has the program, the Game – and then my iPhone I use as my remote control. But really I use 3 computers if truth be told. The first is my desktop, which is where I write my Zwift workouts that you join in on (I don’t need to have Zwift loaded onto my desktop for this).

      On my iPhone I have the Zwift Companion app (from the AppStore), which I use for messaging in The Game (always voice-to-text). And then there's my 4k AppleTV and I have 2 of these: 1 for Zwift ride; 1 for Zwift run. You could do this with 1 AppleTV, but I have a big screen in front of both my treadmill and my bike trainer, and I have an HDMI cable running from my AppleTV to my big screen, and it’s just easier to have 2x AppleTVs. They’re $179 each, and I’m just barely extravagant enough to have purchased 2 of these.

      +

      My big screens? They’re just repurposed TVs. Anything in my home that’s been replaced. Anything that accepts an HDMI or other video cable. However, I did splurge on one new 43” TV and it set me back almost $300. So, the entire hardware buy, for a first class (wo)mancave setup, computer and screen, with a new HDMI cord, is about $500, maybe $600 if you need to mount the TV on a fancy swivel wall mount.

      And that monitor wall mount, that’s what I did for the man-run-cave. But not the man-bike-cave. If you’re riding in aero, which I do, on my trainer, do not mount your TV like you’re mount your computer screen: at horizontal eye level. My screen is near the ground, but tilted so that I can look straight at it. When in aero my eyes are looking at a point on the ground, say, 10 or 15 feet in front of me. Set up thusly, I never need to look straight down. If you fish through a photographer’s raw stock of race pics of pros, in half of those pics the pros are looking straight down. You don’t see these pics in magazines much because that’s not a great image to publish. If you want to look at the screen the entire time when you’re Zwifting in aero, you’ll need to strategize the screen placement.

      +

      The only problems with the AppleTV as the “computer” hosting The Game is: You’ll need to remember your Apple ID; then you’ll need to type in the passwords and IDs using a cumbersome arrow navigation across and around a virtual keyboard. Clumsy but not technically difficult.

      That leaves us with the trainer, and the method to move performance data wirelessly to that AppleTV. If you purchase a smart trainer, you’re buying everything: the trainer you pedal, the device that measures your power output and that intakes a signal from The Game ordering your training to apply a particular resistance to the trainer; and the means by wirelessly sending and receiving that signal. My advice: Ignore the stated retail price of these trainers. They’re becoming somewhat commoditized. Yes, some are better than others. But a fair number of direct drive smart trainers will do the job, enough so that there’s fierce price competition among them. Add to that we’re in between model years for a lot of these brands, the old need to get moved out. If you’re spending more than $800 or $900 on one of these it’s likely you’re overspending, and the prudent shopper can find a good one for $700 and change or perhaps even less, between now and September.

      +

      So, to recap, to be styled, and I mean everything top cabin, assuming you already have an iPhone or Android phone, or one or the other tablet: Smart direct drive trainer: About $800. AppleTV 4k: $179. 40-something inch TV: $300. HDMI cable to connect the AppleTV to your big screen: $40 or so. Done! So, if you have nothing other than a bike and an iPhone, I don't know why you'd want to spend more than about $1,200 all-in, on everything, even if you were hell-bent on spending as much as you could (unless you want to talk Kickr + Kickr Climb, but let's leave that discussion for some other time).

      Oh, and Zwift. $15/mo after your free trial.

      If being styled ain't your thing, and you want to purpose your wind, fluid or mag wheel-on trainer also fine, in which case you need a Speed & Cadence Sensor, which’ll set you back about $60. Your power is emulated, that is, this device makes a guess at your power that’ll work well enough to get you going. The only thing: What we call ERG mode won’t be available to you. ERG mode is a way for The Game to control your trainer, sending a signal to your trainer that you need to pedal at, say, 80 percent of your FTP to keep the pedals turning. This is what we use in “structured training”, which is our version of, basically, a sort of spin class we do (I lead one every Tuesday afternoon, 4pm PST). You’re familiar with ERG mode if you’re already using TrainerRoad, Golden Cheetah, The Sufferfest and the like. Instead of your trainer basically on auto-pilot, you’ve got to change your bike’s gears and maintain the power called for if you use a Speed & Cadence sensor on a dumb trainer.

      We have a 6-part series on how-to-Zwift using a Speed & Cadence Sensor. In fact, it’s a good how-to-Zwift primer if you are using a smart trainer too.

      +

      So, not styled, assuming you're using something newer than a flip phone, and if you have any sort of trainer in your garage, set up cost: $60.

      The one thing that does get people twisted up is if, when you’re setting up your Profile, you place an incorrect FTP in the text box asking for it. What’s FTP? Roughly the max power you can hold on the bike for one hour. When you ride on Zwift, group rides, races, and the like, if Zwift sees that you’re overperforming your programmed-in power Zwift will “help” you by increasing your FTP to what it thinks it should be. When you join our Tuesday Structured Training workout – which is a no-drop workout; if you keep the pedals turning you’ll remain with the group – I’ll tell you, upfront, at the beginning of every workout, how hard you’re going to work. Maybe it’s a tongue dragger. Or it’s moderately hard. Whatever. And if your tongue is not dragging 55 minutes into that 1hr tongue dragger, your FTP is set too low. If you physically cannot pedal the bike during the workout, you’ve set your FTP too high.

      +

      You can drop out of ERG mode – and ERG mode is the best way to ride structured training sessions in Zwift, if you have a smart trainer – at any time during one of these workouts. You can do that either in The Game, or you can do so in the Companion App. If I just confused the hell out of you, here’s a little more detail on ERG mode and how to wrangle it. (It’s very easy.) So, if you’re in the middle of a workout and you just… can’t… pedal! Drop out of ERG for a spell, then drop back in. As long as you pedal, you’ll remain with the group, part of the workout. Then, after the workout is over, lower your FTP by 5 or 10 watts in your profile.

      +

      Now, what I wrote in the beginning was that I was going to give you the larger picture – tell you why I even care about Zwift, or Rouvy, or The Sufferfest, or TR, or any similar platform. It’s because the right platform, used the right way, is a community tool for Slowtwitchers and for Slowtwitch partners, including race directors, clubs, coaches. For most of Slowtwitch’s 20 years my goal wasn’t just to host a virtual community; but to use virtualness to enhance our actualness. Example:

      Are you racing the Ironman 70.3 World Championships in Nice? Do you want to know what that course rides like? Looks like? Uphills, turns, downhills, how hard is it, what gearing works? It would be possible, at least in theory, for Slowtwitch to schedule a group ride, on that bike course, in Rouvy. You could pre-ride any course in Rouvy and because it’s “multiplayer” we could leverage our community and place a group ride on the schedule. If that sounds confusing, it isn’t. It is literally as simple as my saying, “Go here,” and you click my provided link, you “join” a ride and as long as you’re there at that time on that day a bunch of us are going to ride together on that course; Rouvy will make you pedal about as hard as you should pedal in real life on that course; and we’ll all get a sense, together, for whether our individual bikes are correctly geared and so on. That’s Rouvy. Very helpful!

      +

      In the case of Zwift, many of us “meet” a couple of days a week. If I think several moves ahead, Zwift is not for me an end; it’s a means to a different end. I’m asked a lot by race organizers to help them integrate their registrants into processes to help them prepare. Yes, we also maintain lists of local clubs and coaches, bike retailers and the like. Just, for those (including newbies) who want at least 1 or 2 sessions per week where they know they’ll get themselves prepared for the task, we can use Zwift as a tool that provides that. We’ll do that both on the West Coast, upcoming, for those racing the Nautica Malibu Triathlon, and on the East Coast (putting that one together now!)

      If you’re naïve to Zwifting and you want to ask further questions, there’s an evergreen discussion on our Reader Forum where you’ll find help and answers. Here you go if you’d like to sign up for Zwift, and there’s a free trial period if you just want to kick the tires.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/It_s_a_VirtuGo-a-Go-Go__7388.html b/site/slowtwitch.com/www/Indoor_Training/It_s_a_VirtuGo-a-Go-Go__7388.html new file mode 100644 index 0000000..98ae7d6 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/It_s_a_VirtuGo-a-Go-Go__7388.html @@ -0,0 +1,661 @@ + + + + +It’s a VirtuGo-a-Go-Go! - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      It’s a VirtuGo-a-Go-Go!

      + +
      +
      +
      +

      In today’s indoor training landscape, we have a lot of good options to entertain and train - or as I like to describe it, “entertrain”. In the last few years, we have witnessed the rise of several really strong players such as Zwift, TrainerRoad, Rouvy, and The Sufferfest establish their place on the spectrum of structured training and entertainment. Some products - TrainerRoad, Golden Cheetah, PerfPRO Studio - appeal to those dedicated souls who are intrinsically motivated by structured training and seeing the results on race day. Others - Zwift, Tacx, Rouvy, The Sufferfest - may have more appeal to those who are extrinsically motivated by the visual and social aspects of training. No matter where you lie on that spectrum, there are products for you, and the options and quality of these tools are developing at a breakneck pace.

      +

      Today, we’re looking at VirtuGO, a relatively new player in the indoor training space that has its roots in providing solid and scientific structured training combined with entertaining visuals. In this introduction, we’re going to look at what makes VirtuGO stand out.

      +

      Welcome to Pulseville

      VirtuGO is a MMO (Massive Multiplayer Online) game, similar to Zwift or RGT (formerly RoadGrandTours), in which you are rendered as an avatar riding through a virtual world. VirtuGO’s user interface is clean and intuitive - the startup, login, and main menu make it easy to get in, select your ride, customize your avatar, pair up your sensors, and get your legs moving.

      +

      During the ride, the interface utilizes the familiar Head’s Up Display (HUD) approach similar to Zwift, Rouvy, and others. All of your information - power output, where you are, who else is riding, etc. - is displayed. VirtuGO’s interface is somewhat simple, with very few keyboard shortcuts, other than using the number keys to toggle between the five camera views.

      +

      Here’s a brief demonstration video:

      +

      After your ride, you receive a nice performance summary. Strava and TrainingPeaks integration allows you to share your training with your coach or friends. The training aspect is where VirtuGO gets interesting.

      +

      The VirtuGO Performance Pathway

      At first glance, you might look at VirtuGO as being just another game-like product like Zwift. Visually, that’s a natural comparison. But, what separates VirtuGO is the training foundation. While Zwift has done an OK job of adding in features to make it better as a training platform, VirtuGO is starting there: VirtuGO’s core feature is the collection of power-based workouts and multi-week structured training programs designed to target your performance goals. VirtuGO’s library of training sessions and plans is geared towards building up the rider using solid methods over time according to the physiological aspects of riders and riding disciplines.

      +

      VirtuGO calls this their Performance Pathway, and you can read more on it here. VirtuGO breaks down their training sessions and programs in a few different ways, including a Power Profile, cycling disciplines, and training phases.

      +

      Power Profile

      Prominently feaured on VirtuGO's home screen is a graph called your Power Profile. The main objective here is to identify what kind of rider you are, and where your strengths and weaknesses lie. Initially, your Power Profile will be just a big question mark. To develop your Power Profile, just keeping riding in VirtuGO and eventually build it through time and mileage, take a Power Profile Test, or import .FIT files.

      +

      The concept of the Power Profile isn’t new, as folks like Andy Coggan and Joe Friel have been thinking beyond FTP and breaking down cycling performance like this for a long time. As far as indoor training software goes, VirtuGO’s implementation is similar to The Sufferfest’s 4DP, in which a rider’s strengths can be better understood through what kind of power they do best - high wattage short sprints to diesel-like steady-state efforts. VirtuGO uses a bit different way of describing it by using cycling discipline terminology.

      +

      Cycling Disciplines

      VirtuGO uses five cycling disciplines to describe the emphasis on the type of riding:
      • Time Trialist
      • Climber
      • Criterium
      • Sprinter
      • All Rounder
      As the names imply, VirtuGO’s cycling disciplines help identify your strengths and weaknesses in the types of riding you do. The type of rider you are, or the type of rider you want to be, can be influenced and enhanced by utilizing VirtuGO’s training sessions and plans, which are all consistently labeled according to these disciplines.

      +

      The last time I looked at The Sufferfest, one criticism I had of 4DP is that, while it did a nice job of helping you know where you were as far as a power source goes, it didn’t necessarily help you understand how to get to where you want to be as a rider. If you are good at 1 second power, how does that translate to how you ride?

      By breaking it down by cycling discipline, VirtuGO removes a layer of translation to better inform you of how you can adjust your training to be better where you want to be. If you are a climber, or want to be a better climber, you can choose sessions or plans that are designed for a climber. Maybe you have a triathlon coming up soon, in which you would like to focus on being a better time trialer, so you can train accordingly.

      +

      VirtuGO currently provides 33 plans targeted towards the 5 cycling disciplines. Each plan is 4 weeks long, and includes 3 to 5 sessions per week. There are roughly 90 sessions currently available. Pick and choose the sessions you like, or use a plan to guide your training. Sessions can be appropriate for any of the cycling disciplines. Sessions range from 25-100 minutes each, with a majority around the 60 minute mark.

      +

      Training Phases

      VirtuGO also incorporates training phases to lay the rider’s foundation and build on it. VirtuGO uses the combination of cycling disciplines and time-based training to effect change to your Power Profile. They recognize a 5 phase approach to cycling performance:
      • Phase 1: General Endurance Conditioning
      • Phase 2: Quality Conditioning
      • Phase 3: Specific Conditioning
      • Phase 4: Load Accumulation
      • Phase 5: Coalescence
      These phases are incorporated into the training plans and cycling disciplines, not only from a time perspective, but from a prioritization and goal-orientation perspective. For example, while the All Rounder cycling discipline may sound like a good starting point, it’s more of a Phase 3 or 4 level of training. If you’re establish a training base, the Climber plans/sessions are better for someone that is in Phase 1, so you can choose a Climber plan, and follow it up with a Time Trialist or Sprinter plan, which are more in line with Phases 2 and 3. This interaction between the disciplines and phases is explained in more detail on VirtuGO's website.

      +

      It’s easy to compare the VirtuGO Performance Pathway to what has already been done before. For example, TrainerRoad’s greatest strength is their large collection of well-designed workouts and training plans, which they developed early on as a foundation of their product. While VirtuGO certainly doesn’t have the quantity and variety of workouts and plans that TrainerRoad has, they certainly provide a very good foundation - a smart collection of workouts and plans, designed with a proven scientific approach.

      +

      Let’s Get This Party Started!

      In order to enjoy your first ride, you’re going to need a lot of the same things we’ve covered in past articles. When it comes to hardware and connecting all the pieces for an indoor training rig, you may want to refer to our first article on Zwift. VirtuGO is going to have a lot of similarities in the setup and implementation, but with a few things of note.

      System Requirements

      VirtuGO is available for Windows PCs and Macs only. You’ll want a pretty stout system to really enjoy the eye candy, and VirtuGO has a neat tool built-in to determine how well your machine can handle it. The screenshots and videos in this article were done with a pretty powerful PC, running at U4K with the highest settings. Look for another article geeking out on that soon.

      No Virtual Power

      For your trainer, VirtuGO is somewhat limited in selection. VirtuGO does not support virtual power, so you’ll either need to use a power meter or a smart trainer.

      ANT+ and Bluetooth

      As of this writing, Bluetooth support is still in beta, but if you have ANT+ devices, you are good to go.

      Installation is straightforward - download it and install like you would install any other application. VirtuGO gives you a 10 day free trial, and after than it’s a reasonable €5.95 per month subscription.

      The VirtuGO Verdict?

      While VirtuGO has established a really solid base at a reasonable monthly subscription cost, it’s hard not to compare it to Zwift and TrainerRoad - it has some of the best elements of each, but is a couple of years behind. With no virtual power or support for iOS or Android devices, it is essentially limited to riders with a smart trainer and a pretty powerful PC in their pain cave. The subscription price is nice, but are the startup costs a limiter?

      It’s a bit too soon to come to a conclusion on VirtuGO yet. We’ll do another article or two to explore VirtuGO some more before we come to a conclusion.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/It_s_the_Hilly_Vanilli_SlowZwifters_2nd_Weekly_Ride_6749.html b/site/slowtwitch.com/www/Indoor_Training/It_s_the_Hilly_Vanilli_SlowZwifters_2nd_Weekly_Ride_6749.html new file mode 100644 index 0000000..06ab245 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/It_s_the_Hilly_Vanilli_SlowZwifters_2nd_Weekly_Ride_6749.html @@ -0,0 +1,610 @@ + + + + +It's the Hilly Vanilli, SlowZwifters 2nd Weekly Ride - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      It's the Hilly Vanilli, SlowZwifters 2nd Weekly Ride

      + +
      +
      +
      +

      The Hilly Vanilli weekly Zwift ride will take place on Thursday, 10:35am Pacific Time, 6:35pm London time, and every Thursday thereafter. This is the second in the series of “Slowtwitch Indoors” events on Zwift.

      The first of the Slowtwitch Indoors events is simply the nameless Tuesday, 4pm Pacific, weekly workout. It regularly features about 80 to 90 SlowZwifters, is a “rubber band” no-drop Erg mode reasonably stiff workout.

      +

      The Hilly Vanilli is a ride rather than a workout. It will be 70 minutes long, by time. (After Week-1 an evaluation will result in either staying with time or switching to a distance which will still take roughly 70 minutes.)

      This ride is not a race, per se, but there is a little kicker at the end for those who haven’t gotten their money’s worth (we’ll get to that). The Hilly Vanilli begins with a 5-minute, neutralized, warmup. The ride then separates into two groups - a 2.75w/kg (C) and 2.00w/kg (D) group - with ride leaders for each group and a sweep for the D group.

      The groups may ride slightly faster on the climbs - regrouping at the summits - for about 40min depending on the route. The effort levels were chosen after querying prospective participants on the power requirements for the 2 groups (723 people chose his or her preferred group effort).

      Then the dogs are off-leash for the final 20min. What “off-leash" means is up to each rider. For those who think the pace is fine (me), thank you very much, just continue to ride with the group. For those who want to stretch their legs, here’s your opportunity.

      Finish up with a 5min cooldown.

      +

      First week course is the London 8. It's about 20km (12.6mi) in total distance per lap including one stout climb and one rolling climb for net vertical per lap of 238m (780ft). The Hilly Vanilli will probably cover two laps of it. This is a challenging course, which is likely how we'll bias the routes for this weekly ride as we change courses. We'll pick courses that are rolling-to-hilly. Not flat but also not mountainous. Feedback welcome on that.

      Ride volunteers solicited here. We'll need C group leaders, D group leaders, and a D group sweep. Please let us know on the Reader Forum thread if you want to be a ride leader.

      If you have questions about how to join the ride, or you want to know how to navigate Zwift, go to that Forum thread and ask. We also have a 6-part series on how to Zwift, each article getting a little deeper into the experience.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/KISS_Super_League_Dawn_of_a_New_Era__7334.html b/site/slowtwitch.com/www/Indoor_Training/KISS_Super_League_Dawn_of_a_New_Era__7334.html new file mode 100644 index 0000000..928aba6 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/KISS_Super_League_Dawn_of_a_New_Era__7334.html @@ -0,0 +1,629 @@ + + + + +KISS Super League: Dawn of a New Era? - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      KISS Super League: Dawn of a New Era?

      + +
      +
      +
      +

      Racing is the heart of the Slowtwitch community. To compete is human nature. The ultimate test of a person’s mettle or a device’s function has been to step into an arena and pit ourselves against one another to declare a winner. From the gladiators of the Roman Empire to the Olympics to World Cup Soccer to marathons, we’ve found myriad ways to test ourselves against one another, as individuals and as teams. Sport is a major form of entertainment, and superstars are borne of competition.

      +

      Racing is a logical extension of that basic desire to compete. Many of us participate in racing of some sort, whether we toe the line ourselves or enjoy watching professionals compete at a high level. Chariot racing, the Tour de France, the Indianapolis 500, and the Ironman World Championship are just a few examples of how we have combined man, machine, and sometimes beast to test not only ourselves, but our equipment. As the old auto racing adage goes, “win on Sunday, sell on Monday.” In the early-20th century, auto manufacturers saw potential in racing because a winning car meant a perception of quality and, subsequently, an increase in sales. Trek’s rise in popularity and perceived quality grew dramatically globally when a young American won multiple Tours de France on their Wisconsin-bred bicycle.

      +

      The Rise of eSports

      Fast forward to the Internet Age where technology has enabled a new breed of sport to develop: eSports. eSports blend video game culture with traditional sport to enable competition in the virtual digital realm. Gamers compete with one another in person or remotely, and major gaming events such as the League of Legends World Championship draw in professional competition from around the globe. eSports has grown into a billion dollar industry.

      +

      At the same time, Indoor cycling has been evolving rapidly. Trainer and other hardware manufacturers have improved the quality and experience of indoor cycling with better, more accurate smart trainers, power meters, and sensors. Software providers, such as Zwift, have improved their software and provided the infrasructure required to support competitive events. Standardized open protocols such as ANT+ and Bluetooth have allowed hardware and software training tools to communicate. And, of course, more consumers are training and participating in indoor cycling. Essentially, we had reached a point where all the pieces - hardware, software, communications, and people - were in place to allow for professional eCycling to happen.

      +

      The folks at Zwift recognized this opportunity. In December 2018, Zwift announced they had raised $120 million in funding, part of which was to be targeted towards expanding the Zwift eSports platform. Subsequently, the KISS Super League was formed, adding a professional level to the already popular amateur KISS racing league that had already been organizing amateur races on the Zwift platform. The inaugural season started in January, with 10 weekly men’s races. 16 teams were slated to participate, including Pro Continental teams such as Cofidis and Novo Nordisk, UCI Continental teams such as Hincapie Racing, Team Dimension, and Team Wiggins le Col. In an interesting twist, two teams - the Zwift Academy Dream Team and Zwift All-Stars - were included. These community teams are made up of riders who have proven themselves as solid competitors on the Zwift platform, but were not signed to any kind of pro team.

      +

      The men’s league was complemented by a women’s series, which would start a few weeks later, span a total of 8 races, and involve 9 teams, including the two Zwift community teams.

      +

      Of course, if any sport wants to gain popularity, it needs fans. Zwift assembled a broadcast team and leveraged Facebook, Twitch, YouTube, and other social media to allow for fans to watch races live or after the fact. Some of the teams hosted events where their riders were racing in front of an audience, and the KISS Super League broadcast team would be there to interview riders, team personnel and provide additional insight.

      +

      How did the first season go?

      You can see the results of the entire season here. Those of us who tuned in to watch got to see some entertaining and close races, with many races wrapping up with 10 or more riders finishing within seconds of the race winner. Racing was hard and intense, with big wattage right out of the gate. Smart riders knew how to leverage the starts and apply a mixture of traditional and game strategy to put themselves in the position to win. Perhaps one of the more surprising elements was the rise of the community teams: the Zwift All-Stars and Zwift Academy Dream teams both finished in the top 3 in the men’s and women’s leagues, with the Zwift All Stars taking the top step of the women’s podium. Of 18 races, 7 races were won by a member of a community team.

      +

      Regarding the first season, James LaLonde, the Director of Growth Marketing - Cycling at Zwift, had the following thoughts:

      “It’s been an incredible experience working for a brand that’s truly innovative and in a position to contribute to the growth of cycling in such a positive way. As eSports for cycling is still in its infancy it’s been interesting to hear and see how pro riders have approached the style of racing that Zwift offers – from team strategy and course recon missions to the use of power ups. Pros who had more race experience on Zwift had a clear advantage during the early rounds of the KISS Super League (KSL). They know the courses and have a better understanding of how and when to use power ups in comparison to riders who have less experience riding and racing on Zwift. One thing I have to continuously remind myself when watching the live streams is that there are dozens of athletes powering the avatars on screen from remote corners of the world. It’s convenient for pros to be able to participate without having to drive across a state or country or get on an airplane. At the same time, riders are continuously commenting about the intense nature of virtual races and have credited Zwift for improving their fitness when racing outdoors."

      +

      As Mr. LaLonde suggests, indoor racing opens some new doors, albeit virtually. Talented riders who may not otherwise be noticed are getting traditional racing opportunities with teams who see eSports as a means to recruit talent. On the other side of the coin, pros who normally race outdoors are using indoor racing to extend their race season and improve their outdoor fitness and race craft without the costs of travel. Indoor racing also puts pressure on the software and hardware providers to continue to improve the accuracy and reliability of their products. In races that were won by tenths of a second, well within the margin of error of today’s most accurate power meters, fair competition requires a higher standard of measurement. The benefits come back to the rest of us, as consumers, in the form of continual product improvement and a better indoor experience, whether we participate in indoor racing or not.

      +

      So what’s next for professional indoor cycling?

      Where do we go from here? LaLonde shared the following thoughts via e-mail: “We’ve always communicated that KISS Super League is a demonstration series. It’s been a way for us to gauge interest, evaluate potential, and use the project as a general proof of concept. It is by no means our fully-fledged eRacing proposition, but it has provided an opportunity to learn and continuously improve.” He continued, “Season one was launched as sort of a test event for Zwift esports with a view to launching our full offering in 2020. We were able to test different formats and improve our approach to broadcasting. For our next season you can expect further changes in format and an improvement to broadcast and streaming.”

      +

      Indoor training and racing is evolving rapidly, thanks in part to Zwift and others who see the potential in this venture. It’s unlikely indoor racing is going to replace traditional competition any time soon, but by providing another option for those who do race outdoors or for those who haven’t otherwise considered themselves an athlete, the future looks bright for this relatively new, yet complementary, concept for pros, amateurs, and fans of competitive cycling.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Ladies_of_Zwift_the_Unique_Experiences_of_Women_s_Cycling_in_a_Virtual_World_7747.html b/site/slowtwitch.com/www/Indoor_Training/Ladies_of_Zwift_the_Unique_Experiences_of_Women_s_Cycling_in_a_Virtual_World_7747.html new file mode 100644 index 0000000..4bec5af --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Ladies_of_Zwift_the_Unique_Experiences_of_Women_s_Cycling_in_a_Virtual_World_7747.html @@ -0,0 +1,646 @@ + + + + +Ladies of Zwift: the Unique Experiences of Women’s Cycling in a Virtual World - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Ladies of Zwift: the Unique Experiences of Women’s Cycling in a Virtual World

      + +
      +
      +
      +

      I’m admittedly sensitive when it comes to being a woman and a cyclist out on the road. Honks and stares are commonplace, as is being chased down by dudes (always dudes) while in the middle of a workout. I dread passing men during intervals, knowing I’ll hear their freehub buzzing behind me shortly. Then I’ll have to wave them around while I rest, or make small talk over the din of my playlist, or explain that I’m in the middle of something and don’t have time to explain what I’m in the middle of and how I prefer to ride alone. Thank you very much and have a nice day, sir.

      I think being chased by other riders is a phenomenon every cyclist of every gender has to deal with: roadies seem to be equal-opportunity drafters, incapable of letting gaps open in their imaginary peloton. However, my hackles go up specifically as a woman who has experienced plenty of street harassment and worse.

      There was the guy who completely changed his route to tag along with my partner, Dusty, and me on our quest for a Strava segment in favorable conditions. This old(er) man on his old(er) bike hopped right on Dusty’s wheel, separating us, and causing me to fall back out of his draft and our planned rotation. He could have realized he was the unknown factor in our pre-existing dynamic, and recognized that we had no understanding of each other’s handling skills before inserting himself. But no, our plans were ruined.

      Another guy ditched his friend in order to grab my wheel for a solid mile, and then followed me into a parking lot where I tried to shake him, screaming “COME ON LET’S GO KEEP GOING GO!!!” in my face. Did it not occur to him that chasing a woman off the road and screaming at her is not OK, neither in a dark alley, nor on a sunny afternoon bike ride? There is also a man I still see around the neighborhood who called me a cunt, once, for telling him I was doing short intervals and to get off my wheel. No, I did not ask politely. I shouldn’t have had to.

      These experiences, and the reactions I’ve gotten while recounting them to men, remind me of the old “boys will be boys” and “if he pulls your pigtails, it means he likes you” bullshit. How flattering that I can even pull a guy around. I should take the screamed compliments. I should be polite and make friends while minding my own Watts. Just enjoy it.

      Imagine my joy when I found Zwift, and could not only explore a virtual, ever-expanding world, but also complete my workout without having to take into consideration stoplights, bike lanes, gradients, or another impediment to my workouts: men. Zwift was a solitary bubble where I was unbothered by passing and re-passing the same personality-void pixels. Better yet, my little unrealistic and inconspicuous avatar, with slim non-swimmer arms, undefined calves, ratty ponytail, and same-as-every-other-female-avatar’s breasts wouldn’t pique any male avatar’s attention. My avatar even wears the same kit as many male avatars, so we’re like teammates, our mutual respect left unspoken, or maybe just imagined. That’s how I operated on Zwift for a long time, anyways.

      +

      My avatar, circa February. I do not look like this, nor do I know anyone who wears their hair this way while cycling.

      The Social Value of Zwift


      The social aspect of Zwift has really come alive for me recently, though. My awakening to the full functionality of the game happens to coincide with COVID-19 and our new social isolation, but I know others had been taking advantage of everything from group rides to the Zwift Academy long before I was aware of them. It seems to me that the socializing via Zwift is almost as valuable as training on it these days, and maybe especially for women.

      Whereas Americans are still dealing with physical distancing, mandatory mask ordinances and closed coffee shops, Zwift provides a fantastic outlet for tri and cycling clubs missing their group workouts. Patti Flynn (Level 15) had just hit her stride, training with a group twice a week in the Computrainer studio at Edge Athlete Lounge through Chicago’s winter, when COVID-19 shut down the facility. Her “Girl Gang” had spent early mornings sweating it out, and anyone who has participated in team sports knows how intimately bonded you become to people when your heart rates rise together before the sun does. After the shutdown, Ms. Flynn and her Gang missed each other so much that they hooked up a network of Zwift, Zoom, FaceTime, and other connectors so they could continue to ride “with” each other through Chicago’s early spring and quarantine.

      Likewise, my in real life (IRL) epidemiologist friend, Stephanie Melkonian Linkus (Level 9) recently joined Zwift “as a COVID alternative to group rides” for Trail Dog Tri, the triathlon club she founded in Albuquerque, New Mexico. She had previously been set up on TrainerRoad, and actively resisted joining Zwift, but the social outlet Zwift provides became inescapable for her and her clubmates. Dr. Melkonian is able to kill multiple birds with one stone: through Zwift, she can maintain semi-normal operations of her club, get her own workout in, socially connect with other people –teammates, athletes, and other women— enjoy some maskless exercise (since New Mexico instituted a mask mandate while outdoors), and hopefully relieve some occupational stress during this extremely demanding time.

      What’s especially great about riding on Zwift is that, IRL, I might see one other serious woman cyclist on a three-hour ride out my front door. However, there are oodles of women on Zwift at any given moment. The mere sight of a female avatar makes me happy: somewhat ironically, while I expected us to be indistinguishable and unremarkable pixels, women on Zwift do catch my eye, and I have actually managed to simultaneously make friends with a simple Ride On, or by complimenting a creative kit/sock combo, all while minding my own watts. Through in-app messages, one woman –who was in the middle of racking up 100 miles on Watopia’s Tick Tock route—told me about a virtual challenge series her favorite race director had organized. Another woman and I bonded over our hometowns, and invited me to visit next time I am in her area IRL. We still send Ride Ons when we see each other in-game.

      My local bike shop hosts (or hosted, pre-COVID-19) one women’s ride a week, I think, as far as I know—I could confirm this but that seems almost as hard as mustering up the energy to show up on time for said ride. However, there are multiple women’s-only events held every day on Zwift -- including this site’s own Ride of the Valkyries on Tuesday mornings. My friend Kate Baldwin (Level 12), who lives 9,336 miles and tomorrow plus 15 hours away, in Perth, Western Australia, talked me into doing a few races with her—something I’m not brave enough to do IRL. After a few co-ed crits where we finished mid-pack and had very “meh” experiences, we started preferring women’s-only races. As long as my voice-to-text can interpret me through my TV, fan, and desperate gasps for air, we’re able to use messages to form groups with other women, work together, encourage each other to bridge up to the next group, and compliment each other’s efforts. Often, we bond with one or two new riders, then follow each other on Zwift or Strava afterward.

      Co-ed races, by contrast, can be near silent, with no banter at the start or finish, and definitely no coöperation during the race. I’ve often been the only one “chatting,” asking riders near me to maybe group up and help each other out for 45 minutes in the middle of the Ven-Top climb, for instance. What’s especially funny is that my first triathlon was an all-women’s event, and the same attributes: on-course chattiness and seemingly needless segregation of the sexes—turned me off of such “lady power” events. My personality and preferences in Zwift are as unrecognizable as my avatar.

      Pro triathlete Amanda Wendorff (Level 50) experienced the same type of awakening to women’s bike racing via Zwift, albeit at a different level than the average woman Zwifter. “Honestly I had no bike racing experience, so it has opened my eyes to a whole different world and really created a desire to get out there and do some bike racing once the world opens up a bit. It's also made me more aware of women's cycling, generally, and I've become more of a fan of the ladies at the top of that sport.” She also described the difference between women’s and co-ed racing better than my “meh:” “The co-ed races, which really means the men's races, with a woman or two mixing it in, tend to start a bit harder. Zwift really seems to favor the higher power, heavier riders, and I'm just barely hanging on. When I ride in the men's races, I'm just redlining it the whole time, attempting to stick with the pack as long as I can. The women's races have more depth, and while they also start fast, they'll usually settle down a bit, and then the fun becomes anticipating, seeing, and responding to attacks.”

      Ms. Wendorff and her ‘A’-level W/kg dove further into the opportunities Zwift provided, discovering the highly competitive World Tactical Racing Leagues’ (WTRL) Team Time Trial Series. “I quickly reached out to a couple of my QT2 teammates, Angela Naeth and Lenny Ramsey, who were similarly loving the Zwift racing opportunities, and we created Team SISU. We set about recruiting friends and colleagues from the racing circuit, and ended up with a fantastic team of mostly pro triathletes (myself, Angela, Lenny, Meredith Kessler, Carly Johann, Tamara Jewett), one super star amateur (Lucy Brash), and more recently, one of Zwift's best pure cyclists in Pamela Levine.” QT2 coach Tim Snow “has been serving as our directeur sportif, which is enormously helpful.” For anyone who has been tuning in to the race series on Thursdays, you know Team SISU as the perennial runner-up. Ms. Wendorff says the team has its “sights set on eventually providing a legit challenge to the mighty Socks4Watts team, an all-star team of cyclists and triathletes that has been dominating the women's league.”

      For her part, Carly Johann (Level 19), had a Zwift account for a few years, but didn’t really engage with it until COVID hit. A few months after the birth of her second child, just about when she was ready to get back outside for training rides and runs, COVID hit. “All of a sudden, I had no race season and was a stay at home mom with two kids who were here all the time. Enter Zwift. I started dabbling in some rides without really understanding the potential. But my daughter loved the way it looked and wanted to ride. So we put her training wheels in some sneakers and got her set up to ride next to me. The baby often lays on the floor in front of us and watches too. These ‘family’ rides don’t last long, but they’re better than nothing. Then I started realizing there were segments to get and races to enter. All of a sudden, a race season (albeit a very different one) materialized and I had something to look forward to each week. Then I started realizing there were segments to get and races to enter. All of a sudden, a race season (albeit a very different one) materialized and I had something to look forward to each week. I joined Team SISU and we quickly melded into a pretty solid TTT team. I did some of the Zwift pro Tri crit races and started looking for races every time I logged on. Because of time being tight, I usually have an hour to exercise solo each day, but it’s always at different times during the day. It’s impossible to meet up with friends locally for physically distant rides, but it’s always a bonus to log into Zwift and see someone I know riding. I’ve even made new “friends” who I’ve never met before through racing with Team SISU. That hour of exercise feels so productive when it’s a TTT or solo race.” Ms. Johann, a professional triathlete, has parlayed her quest for post-partum fitness into a 4.6 w/kg and being a top contributor to Team SISU’s success.

      +

      According to the WTRL commentators a few events ago, approximately 3500 riders participate in the TTT series, over 600 teams, with about 600 women across 68 teams participating. I suspect and hope that women’s cycling will experience a Renaissance in a post-pandemic world. And according to a WTRL spectator I chatted with on YouTube, “the women’s races have been more fun to watch.” Get ready, USAC.

      Ironman has barely been able to scrape together a field of women to show up to and take 10-deep prize money, but the ZPRO field attracts a stellar field week after week. Pro triathlete and Ironman and 70.3 champion, Jackie Hering, (Level 24), was extended an invitation to the ZPRO Series thanks to the advocacy of Meredith Kessler, and thus has had a shot at continuing to earn prize money through COVID-19. A working mom of two, Jackie has enjoyed the opportunity to race “women from all over the world, with no cost or travel commitment, and we can race from home.” Challenges therein include occupying the kids with snacks or activities with their dad, because “it can be very tough to try to manage small kids while going max bike effort and having to keep an eye on the screen to avoid getting dropped.” She has also gotten to know a number of fellow pros she wouldn’t have during her normal tri-season circuit: “I’m on a message thread with a few ladies in Europe I have not raced much, but we were together in most Zwift events. It definitely hit the racing feel we were looking for!”

      The Negatives of Zwift Becoming More Realistic


      Unfortunately, despite the many positive aspects and eye opening experiences we’ve had as women on Zwift, as the virtual world has become more realistic and added features, the interactions have likewise become more like those out on the IRL roads. We’ve all seen the comments and off-color sexist jokes being told, openly and publicly, via Zwift messages. I’ve rarely seen anyone ask it to stop. Every woman I’ve spoken with on the topic told me stories of Watopian casual sexism, but each had an IRL equivalent women cyclists are all too familiar with: men “drafting” off them, or yo-yoing with them, without so much as a courtesy Ride On. OK, not everyone knows how or is able to use the Zwift Companion app, but occasionally men reveal themselves by issuing Ride Ons, or chatting with each other, but not with the women amid them.

      For example, I was riding in Watopia’s Desert Flats, once, and had just earned the sprint jersey there. While I was feeling pretty proud of myself, donning my new jersey, a fellow within view messaged “Does anyone know what that bull skull kit is from?”

      “Well, I do,” I said, while being the only person wearing said bull skull jersey. Is it harder to say “hey K. Langer how did you earn that jersey?” Anyway, I volunteered the pertinent information.

      “LOL.”

      Then nothing. No “cool” no “thanks for explaining!” and not even a Ride On. Give the Ride Ons, dudes. There’s no bonus for a high ratio of Ride Ons received vs. given.And have a great day, sir.

      Patti Flynn of the aforementioned Girl Gang has experienced the same casual sexism. “Ironically, it’s affirming, in a way.” As a trans woman, being the recipient of the same treatment that many other women know is all part of the experience of living, biking, and existing as a female rider on Zwift.

      Janine Willis (Level 45), multi-time amateur Ironman 70.3 World Champion, Ironman champ, and Kona podium finisher, told me a number of horror stories. One of her many times logging onto Zwift, she was immediately verbally assaulted, via in-app group messaging, by a man she doesn’t know IRL or virtually. “He was using inappropriate language and making rude statements and was trying really hard to get my attention. Everyone could see [his comments], and he was using my name, so I knew they were directed at me, alone.” No one else commented or stepped in. “I don’t know if there is a way to report harassment to Zwift, there probably is. Sometimes it’s just easier to walk away, modify your session or call it a day.” How is it that we can’t avoid being run off the road and screamed at, even in a virtual environment? There’s no easy block and report button, so Ms. Willis had to log off and wait for the offender to end his ride, sacrificing her own schedule and workout. Zwift now censors language with asterisks (which you can undo in settings) but that’s not helpful when you’re being verbally assaulted.

      When one finishes her ride, a summary of achievements pops up, along with a list of people she rode amid for an unknown amount of time. I’ve never mashed the follow button for any random riders –only those with whom I chatted or raced. Yet, Kelly Burns Gallagher (Level 36, you know her) and I, for two, often finish rides to discover multiple follow requests from random dudes (always dudes). I can’t speak to men’s experiences with this, but this has happened IRL via Strava, too. One of my most ardent Kudos-givers is someone who wordlessly rode past me on a local trail, once. Another is someone I’ve “seen” on Zwift, once, and crossed over to Strava. It’s generally harmless, and I’ll take all the thumbs-ups I can get, but consider our hackles in the upright and locked position, wary of the behavior crossing the line into stalking.

      Body Type Diversity -- or the Lack Thereof


      This story started out as one where I would discuss how I, as a woman, felt about Zwift’s hairstyle options as well as the strange ectomorphic body type we’re given as female avatars. Yes, our avatars’ physical appearances is a conversation I’ve had with almost every woman I’ve spoken to for this story. In light of all of our riding experiences, both good and bad, it seems petty to complain about whether or not our avatars visages are realistic, insulting, or aspirational. However, this isn’t about vanity: it’s about whether the programmers at Zwift value the diversity in their ridership enough to depict it.

      I look around and see a number of body types for men –from tall and slim to downright bodybuilder buff. Men have calf muscles and triceps. One guy I’ve done meetups with has full-on Terry Crews-esque lats and delts. As triathletes, you all know the various body types –tall, short, slim, muscular, androgynous, vascular, curvy—that we encounter in the sport. Where are they on Zwift?

      +

      I invariably think, too, of my friends put “amputee” or “handcycle” or “age 77” in their handles to represent themselves, but have no way of doing so in-app. Nick “Run Nick Run” Roumonada (Level 33), a below-the-knee amputee, multi-time Ironman, CAF ambassador, and medical representative for a prosthetics company, says “it would be amazing if they had hand cycle, push rim chair and an amputee option. It wouldn’t have to be crazy specific –even the [emojis for prosthetics] work.” Mr. Roumonada said he has floated his idea to the powers at Zwift to no known affect. If Zwift can create a concept “Tron” bike, a Big Wheel, dinosaurs and Sasquatch, or top hats for New Year’s rides, why can’t the programmers also whip up some better representation for non-able bodied white males with typical haircuts and colors?

      At any rate, I re-affirm that I’m sensitive to gender disparities in experiences, both real and virtual. There are probably a lot of women out there who have not felt the same the highs and lows of Zwifting as those told herein. However, I hope that in hearing a few stories, more men will respond to and respect women while cycling: will consider how, for some, it’s not just a game or a ride, but an escape, a brand new skill set, a chance to socialize and learn, and a form of freedom from barriers that may exist in real life.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/On_Zwift_Every_Day_is_Race_Day_6710.html b/site/slowtwitch.com/www/Indoor_Training/On_Zwift_Every_Day_is_Race_Day_6710.html new file mode 100644 index 0000000..510825e --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/On_Zwift_Every_Day_is_Race_Day_6710.html @@ -0,0 +1,648 @@ + + + + +On Zwift Every Day is Race Day - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      On Zwift Every Day is Race Day

      + +
      +
      +
      +

      [Note: This article was originally published over the 2017/18 Winter season, and has been updated to reflect changes over the past 12 months. The list of articles, in order, is at the bottom of this article.]

      We’ve explored Zwift's many features to entertain, motivate, and train you, and now it’s time for the real test - it's time to race!

      To be clear, most Slowtwitchers aren't Zwifters because they like to virtually race. But you might have the talent! Here's a Slowtwitcher that's an accountant by day, and a Pinball Wizard in his pain cave! (And you may be hearing more from Matt Gardiner, in these pages, about future Zwift racing opportunities.)

      Racing in Zwift is treated as an "event", so the first thing to do is look at the Zwift Events page to see what's coming up. As you'll see, every day is race day - there are races being held several times per day, with a few different major series providing events of different durations for riders of all levels. A majority of the events are criteriums, but there are also Fondos and Time Trials available, too. There are no race fees or waivers to sign - just show up and run what ya brung.

      Racing in Zwift supports the use of categories based on your watts per kilogram at FTP. For example, if you weigh 75 kg and have an FTP of 300, your power-to-weight ratio works out to be 300 watts / 75kg, or 4.0 watts/kg.

      +

      Generally, there are 4 categories:

      A - 4-5 watts/kg
      B - 3.2-4 watts/kg
      C - 2.5-3.2 watts/kg
      D - 1-2.4 watts/kg

      It is up to you to select the appropriate category when you join an event. When you look at the Zwift Events page for the race you are interested in, make sure you read the rules. Every effort is made to keep things fair. Some events won’t rank you in the results unless you have heart rate data, while others require a smart trainer because of the more accurate representation of your power. In some races Power Ups are allowed, while in others they are frowned upon. In the virtual world it could be easy to cheat, and the onus is on you to be honest and report your data as accurately as possible using the most accurate devices you can. It can also behoove you to make sure you are getting all the credit you deserve.

      OK, enough of the rules, blah, blah, blah... my race starts in 10 minutes - let’s go!

      You’ve picked a race, determined your category, paired your sensors, and did your pre-race ritual. Now you need to show up at the start line. Joining a race in Zwift is pretty easy. Log into Zwift like normal and look for the event on the top corner of the Start screen. You need to be very specific when you click the event to join - if you just click the event box, you will be placed in the A category, which is a really humbling experience (just trust me on that one). You need to click your specific category. Honestly, this is very difficult to do with clumsy, sweaty fingers on an iPad Mini. So, pay attention here.

      Once you’ve selected your race and started your ride, you can just ride around the course to warm up. In the lower left corner of the screen, you will see a button to take you to the start line. As the race approaches, you can go to the start line and continue your warm up on a virtual trainer.

      +

      Make sure your audio is up when you are warming up - some of the race organizers have enhanced the experience by providing some intro audio commentary. You might chat with your fellow racers a bit to work off the pre-race jitters, but when the countdown hits zero, it’s on!

      Like other Zwift events and real races, you’ll find that you need to find your place in the pack right away. It can be a bit crazy, since you are going to have a mix of riders of many abilities all starting at once. It's easy to get caught up in the moment and overcook the first lap. Having said that, Zwift races can start very fast; faster than outdoor bike races; to weed out the startled.

      In the list of riders on the right side of the screen, you can see which category each rider has selected, so you may try to latch on to the wheel of someone else in your group. About latching on: Depending on the race, "draft" is usually enabled, and there's a feature called "doubledraft": twice the drafting benefit. In doubledraft, it's that much more important to try to stick with the group. Note: If you're chosen (regardless of what you're actually riding) a TT bike in your Zwift Profile, that bike is slightly faster when there's no draft, but that style of bike doesn't enjoy the benefit of the draft. Some races don't allow TT bikes. But if you're hoping to draft, you don't want to be "riding" one anyway.

      Developing your racecraft is all part of the Zwift virtual racing experience! There’s no doubt it can be challenging - and sometimes frustrating - to get everything to line up the way you like. That's racing. Just don't forget to have fun.

      +

      Virtual racing can be quite humbling. In the screenshot above, I accidentally classified myself as a Category A rider (yeah, somebody wasn't paying attention…) when my power-to-weight ratio is really more like the back-of-the-C-pack. You can see the classifications of the other riders next to their country flags in the rider panel on the right side of the screen. In the races I've done so far, I have finished more toward the middle of the D pack. In this shot, I found a couple of other riders to work with, which was a lot of fun.

      Once your race is done, you will get a results screen showing the overall rankings:

      +

      Unfortunately, the results won't stick around for long and Zwift doesn’t provide a way to see your results after you click the OK button. Like many of us who enjoy competition, you may want to be able to analyze your results, do a bit of bench racing, and plan to come back with a vengeance in your next race. This is where Zwift has a really active community that has surrounded the platform and provided some great grassroots enhancements - you can see your results on Zwiftpower, which has the potential to become the Athlinks of the Zwift virtual racing world.

      If you create an account, you will be ranked according to your results (just, maybe best to create an account before you race if you want to be ranked). Zwiftpower requires you to race at your level, so, if you select C category but you’re A level, Zwiftpower will disqualify you. Please note that Zwiftpower is not Zwift, so, if there’s an issue with results in Zwiftpower, you need to contact the site organizer; Zwift can’t help you.

      There is also a ZADA Group. Yes, The Zwift Anti Doping Agency. (Which you knew was coming, didn’t you!)

      There are both community-organized and Zwift-organized races and race series with real prizes on the line, but they usually have a list of requirements. Remember higher up, when I mentioned the occasional rule? The two most common for races with prizes are that you must be using a power meter or smart trainer (no zPower) and you must be wearing a heart rate monitor. There's also often a stipulation of "no TT bikes".

      It's time to settle in to the post-race party

      Just like in real life, racing in Zwift is going to bring out the best and worst of you and your equipment. Your racing experience depends primarily on your ability to execute, but you can certainly enhance the experience with the right hardware. In our next and final article, we will touch on a few ways to enhance your Zwift experience, whether you are racing, training for a race, or just enjoying the views in Watopia.

      +

      Here's the whole series:

      1. Welcome to Watopia: Install Zwift using virtual power, the most cost-effective option.
      2. Zwift, The Deeper Dive: One more layer below the surface.
      3. It's Game On With Zwift: Zwift is a game, but also a virtual community.
      4. Zwift: Let's Dance! The social aspect; Zwift is a Massive Multiplayer Game.
      5. Zwift: A Potent Tool for Training: Zwift’s workout mode.
      6. On Zwift Every Day is Race Day: Zwift's racing culture and community.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Pain_Cave_Displays_7600.html b/site/slowtwitch.com/www/Indoor_Training/Pain_Cave_Displays_7600.html new file mode 100644 index 0000000..33ef8a6 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Pain_Cave_Displays_7600.html @@ -0,0 +1,647 @@ + + + + +Pain Cave Displays - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Pain Cave Displays

      + +
      +
      +
      +

      There’s a thread on our Reader Forum right now about how to configure the Pain Cave displays. I’m going to show you some pics of my workshop and how I’ve set it up, after a lot of trial and error. I’m beyond certain there are better, cleaner, sexier Pain Caves out there. I’m not the Cave expert. Maybe a Pain expert.

      Let me start with the following exhortation. Run Zwift off of an AppleTV 4k if you want clean and cheap. This is a computer. It’s $169 if you pay full list price.

      +

      Just, make sure it’s a 4k. AppleTV 4k. There’s a non-4k and you don’t want that. And no, an AppleTV 4k isn’t true 4k.

      If you want the full gaming rig on which to run Zwift, that’s a nice idea and our Travis Rassat explains how he built his. But you get a pretty full featured Zwift experience with the AppleTV 4k.

      I have one of these AppleTV 4ks for every “workstation” in my workshop and at present there’s four of them. The workout devices you see in the images here are not mine. They’re loaners. I own only the displays, the AppleTVs, and the cables. Alas, I don’t own the actual smart trainers and the treadmills. They rotate in and out, and I write my reviews.

      +

      But there are some rules or at least best practices. An AppleTV assumes you’re going to use a separate display, and you can get a 50” TV set for $300 and a 43” for half that. TVs are cheap. There’s no real good reason not to just go AppleTV 4k and a large TV dedicated to every workout device: treadmill or smart bike trainer. One nice benefit to this: You’re set for company. If you have a friend over, and he or she brings a trainer, the AppleTV + TV set + HDMI cable for a friend will set you back $300 to $500, depending on whether you’re a big or medium spender.

      Now, about that TV and where to mount it. I favor just behind the treadmill, if for running, with the bottom edge of the TV a little below the top of the treadmill’s display. Remember, your head is well above the treadmill display, and you’re looking a bit downward. My monitor looks like it’s mounted a little low until you’re up on the treadmill, running, and then you see that it’s just about right.

      +

      I use an articulating wall mount. I don’t favor a tripod. I can pretty much guarantee you’ll have a TV “event” if you go the tripod route. I know whereof I speak!

      If you can’t mount it on a wall, then I recommend something like this TV cart from Uline, at just over $200; here’s one for about $20 less; and here’s another that’s downright affordable. A Forum User on that thread found one at Walmart for $69.

      +

      For cycling, my TV sets are placed on or close to the floor. If I’m riding a tri bike, then it’s pretty much at floor level and tilted back. If I’m riding a road bike, I might have my TV a little higher, maybe a foot off the ground, and tilted back but not quite so much.

      At left is a tri bike, mounted on a Saris H3 smart trainer, the whole thing sitting on a Saris MP1 platform. The TV is almost at floor level because it's a tri bike (a Cervelo P3X). In the center is a Wahoo Kickr Bike, it's a road bike, and that's why the display is a little off the ground. I just want the TV to be looking squarely at me when I’m looking squarely at it. At right is another treadmill, a Trueform, and you can just see the bottom of the monitor, mounted just below eye level when I'm on the mill.

      My Pain Cave is not especially pretty, but this is my set up for testing treadmills and smart trainers. This is the relationship between device and display that's easiest for me.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Press_Release_Zwift_Takes_Things_Off_Road_7521.html b/site/slowtwitch.com/www/Indoor_Training/Press_Release_Zwift_Takes_Things_Off_Road_7521.html new file mode 100644 index 0000000..c3b06c8 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Press_Release_Zwift_Takes_Things_Off_Road_7521.html @@ -0,0 +1,649 @@ + + + + +Press Release: Zwift Takes Things Off Road - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Press Release: Zwift Takes Things Off Road

      + +
      +
      +
      +

      Today is destined to be an exciting day for those of us who enjoy riding off-road and in Zwift: Zwift is introducing two new training plans aimed at those who like to play in the dirt. Please read the press release below.

      +

      Press Release: ZWIFT TAKES THINGS OFF ROAD

      Zwift, the global online training and racing platform for athletes, has today added increased functionality for off-road cyclists.

      Building on the success of a successful partnership with the Absa Cape Epic earlier in 2019, Zwift is looking to further broaden the appeal of indoor training for mountain bikers and gravel riders. Off-road cyclists already account for 10% of Zwift’s user base.

      “We already have a strong user base of riders who spend most of their outdoor riding off-road.” Says Eric Min, Zwift CEO and Co-Founder. “This may not be a total surprise - indoor training isn’t just an effective training tool for road cyclists. However, this first coordinated move to cater for the audience is a significant step. For the first time, we will have dedicated training plans and bikes that suit the needs of off-road cyclists.”

      Indeed, the game update, released today, contains two new training plans designed to meet the needs of off-road cycling where the specific demands of power output and pedalling technique differ to those when riding on the road. Adding to the sole Gravel Grinder training plan, is the new ‘Pebble Pounder’ plan. The Pebble Pounder is designed for the entry level plan aimed at those looking to make the leap into gravel events. For mountain bikers, the intermediate ‘Dirt Destroyer’, designed for the intermediate rider who has a developed endurance base, but is looking to improve their mountain bike specific strengths through repeated surges far over threshold to increase anaerobic capacity, and grow their peak pedal torque to help smash up even the steepest of climbs. A second, more advanced, training plan for Cross Country cyclists will follow early in 2020.

      In addition to the new training plans, Zwift has also released a new fleet of bikes to the game which can be found and purchased in the Drop Shop. Zwifters can use their ‘Drops’ - Zwift’s own currency that is earned through riding in game - to purchase the new Canyon Grail, Inflite and Cervelo Aspero gravel bikes. In addition, mountain bikers may choose from the Canyon Lux, Scott Spark and Specialized Epic.

      These bikes will offer more than just visual appeal. All bikes in Zwift now have riding characteristics designed to better align them to their real world equivalents. Gravel bikes are fastest on gravel, moderate on road, slower off-road. Mountain bikes are naturally fastest off road, slower on gravel and slowest on the road.

      Further updates and new partnerships will be announced in the coming months.

      +

      About Zwift

      Zwift is the fitness company born from gaming. We’re dedicated fitness enthusiasts that also happen to be experienced software and video game developers. Combining that passion and deep understanding of the fitness world, Zwift is the first company to use massive multiplayer gaming technology to bring the outdoor experience indoors. Athletes from around the globe can train and compete with each other in rich, 3D-generated worlds simply by connecting their existing devices & hardware (e.g. cycle trainers, power meters, treadmills, heart rate monitors, etc) wirelessly via open industry standard ANT+ and BLE. From friendly competition, to racing & structured training programs, Zwift is building a community of like-minded athletes united in the pursuit of a better social fitness experience.

      +

      More to Come

      As the new year rapidly approaches, we are working on extensive coverage of all things related to structured workout and training plans. Along with articles planned to help even the most novice indoor cyclist get started with a focused training plan, we’ll be exploring these new off-road plans and features in Zwift within the next few days.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Revised_Expanded_Zwift_Rides_6927.html b/site/slowtwitch.com/www/Indoor_Training/Revised_Expanded_Zwift_Rides_6927.html new file mode 100644 index 0000000..1170572 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Revised_Expanded_Zwift_Rides_6927.html @@ -0,0 +1,645 @@ + + + + +Revised, Expanded Zwift Rides - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Revised, Expanded Zwift Rides

      + +
      +
      +
      +

      We are adding a Zwift event on Friday morning; and we're moving a Thursday ride from morning to evening (and we're changing its format).

      And we're going to change the Thursday mid-morning ride to an evening ride (and change its format).

      +

      Effective immediately, and this includes tomorrow's ride, Slowtwitch's Zwift rides are:

      Tuesday Structured Training: Tuesday Afternoon, 4pm Pacific. 60-70min. This is the eponymous no-drop event that regularly features between 60 and 90 Slowtwitchers.

      Hilly Vanilli: Thursday Evening, 6pm Pacific: 50-70min; this ride now will feature 3 "waves": 5:50pm D; 5:55pm C-; 6:00pm C+, and all these are Pacific Time. The goal is to see if the later leaves can finish with the earlier, and if the earlier leaves can hang on with the later if/when caught.

      Festina Lente: Friday Morning, 8am Eastern, 60min, Structured Training. Festina Lente is Latin for "make haste slowly". It was the other finalist, in 1986, when I was deciding what name to call my new wetsuit manufacturing company. "Festina Lente" lost out to "Quintana Roo". Now, 32 years later, I finally get to use it.

      If you click the links above through the Zwift Companion App you'll go straight to the event, and can "join" the event there.

      +

      For those of you naive to Zwift, here's our primer, beginning with the first of several installments.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Rollers_5555.html b/site/slowtwitch.com/www/Indoor_Training/Rollers_5555.html new file mode 100644 index 0000000..68e3f49 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Rollers_5555.html @@ -0,0 +1,631 @@ + + + + +Rollers - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Rollers

      + +
      +
      +
      +

      Choosing a bike trainer has become as complicated as choosing a bike. One can choose rollers, wheel-on, or direct mount trainers, magnetic or fluid resistance, and finally smart or dumb trainers. These categories don’t even address feature points like noise, performance, size and portability, and cost. Ironically, the least talked about trainer is not only the most simplistic; it is also meant for the most hard-core cyclists: Rollers.

      +

      Rollers are the psychic opposite of what most appeals to triathletes. This is the category of stationary bike trainer that least given to technological advancement. We have running shoes (e.g., Altra Halo), sunglasses (e.g., Recon Jet), and even swim goggles (e.g., Swim Iolite) that are more sophisticated than the simple roller.

      +

      + +

      + +

      Once a cyclist gets past the initial thought of a roller being an antique, it is according to some the most effective indoor bike trainer available.

      +

      + +

      +

      The basic roller consists of three metal or plastic drums, two bars, and couple of bolts. It operates in a very simple fashion: Keep pedaling or fall off! It may take a couple of times to get use to the feeling and balance of riding on rollers but the basic roller is a staple of quite few cycling traditionalists. A quick tip first time users: Consider starting in a doorway or an area with a soft landing.

      +

      + +

      +

      Some brands provide a basic roller with a fork stand such as the Omnium Trainer (reviewed here, pictured above) but this takes away from many of the benefits of purchasing a set of rollers in the first place.

      +

      While simple by design, technology has elbowed its way into rollers with resistance units and there are even some smart rollers. One of the first resistance-based rollers was the Kreitler Kat 1 and unlike most of the current resistance based rollers which use magnetic resistance, the Kat 1 uses a flywheel with a headwind fan to mimic air drag.

      +

      + +

      +

      With most of the talk focusing on smart trainers (i.e. trainers that can automatically control resistance), it is no surprise that Elite, a very popular European brand, has a smart roller called the Real E-Motion B+. It looks interesting, but is not currently available in North America. See here for an explanation of the confusing licensing agreement between Eite and Inside Ride.

      +

      + +

      +

      There is little cost savings versus a wheel-on trainer and a nice set of rollers as both can run you close to $300 and more. There are a number of well-known brands producing rollers, including Kurt Kinetic (below), Tacx, and CycleOps, along with many others.

      +

      + +

      +

      There are a number of advantages when comparing rollers to wheel-on and direct mount trainers. Rollers better simulate actual riding, arguably are more efficient, and they provide less stress on the structure of the bike as it can move freely but they are not without disadvantages as well. Most rollers do not have the ability to control resistance to the same extent as wheel-on or direct mount trainers and although the ability to move freely can be considered an advantage, the risk of dismounting your bike in a manner worthy for you YouTube is also present.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Rouvy_You_ll_Like_the_Movie__7132.html b/site/slowtwitch.com/www/Indoor_Training/Rouvy_You_ll_Like_the_Movie__7132.html new file mode 100644 index 0000000..c95bba2 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Rouvy_You_ll_Like_the_Movie__7132.html @@ -0,0 +1,654 @@ + + + + +Rouvy! (You'll Like the Movie) - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Rouvy! (You'll Like the Movie)

      + +
      +
      +
      +

      We’ve been having some fun exploring the features of Rouvy’s video-enhanced routes, which make up the primary function that separates Rouvy from some of the other indoor training software packages on the market today. Being able to experience rides from around the world from the comfort of your own home is entertaining and for many of us, that is more than enough motivation to keep us on the bike inside when the weather isn’t cooperating outside.

      But, now it’s time to get to work. Let’s get back to the root cause of indoor training or, to put it another way, why the concept of indoor training came to exist in the first place: preparing for an event.

      +

      Riding courses in Rouvy is fun, but not necessarily especially disciplined. You averaged 16.8 mph? Great! But what does that mean? At best, riding courses will confirm your current level of fitness.

      When it comes to maximizing the effectiveness of your training in preparation for a race season or a specific event, we need a bit more - we need a quality workout and schedule, and we need effective management through order and context. To improve, we need immediate feedback and ongoing information - what gets measured gets managed.

      Rouvy’s video routes are the icing, but now we’re going to bite into the cake and look at what Rouvy provides in terms of their workout functionality.

      Workout Selection and Search

      When we first land on the Rouvy dashboard, we have a variety of options presented to us in the left menu. Today, we’re going to concentrate on the WORKOUTS section. We’ll look at searching for, executing, and reviewing a workout. There are a couple of ways we can approach how we choose our workout.

      You may be the person who rides for fitness and fun and likes the idea of structured training like you find in a spin class - there are intervals of varying intensity and the workout more or less stands on its own. This might be a bit more spontaneous and based on how you feel at the moment.

      If you are serious about your training efforts for an event, you’ve probably done some work in advance and know what you are looking for in your workout. Structured training is not nearly as spontaneous as riding couses can be. It requires context - the workout you do today exists as one small, yet critical, cog in the machine that is your race preparation. Race day might be several months off, but this workout is an important piece of the puzzle. Each workout is a part of the plan for the week, and each week is part of a bigger plan preparing you for your event sometime in the future.

      +

      Rouvy’s Workout search is similar to the Route search - when you land on the All Workouts page, as shown above, you’ll see a selection of categories. As in the route search, these categories provide an easy way to find workouts that have been tagged accordingly, such as the CycleOps workouts category shown here, from the Windows and iOS (on an iPad mini) versions, one pictured just above and one just below:

      +

      The issue with the categories is that, at first glance, they suggest a sense of a library-like order, but they actually obfuscate the depth of the Rouvy workout selection. There are thousands of workouts in Rouvy, but you’ll have to dig a bit to see what’s there. Any search is limited to a maximum of 100 results, so you need to be a bit more specific about what you are looking for if you want to find it.

      Another way of finding your workout is the web-based version of Rouvy’s workout search:

      +

      The online search is worth consideration because we can be more specific regarding the workout duration, TSS, and IF, so we can further narrow down our results. In either search tool, you can always bookmark a workout for later - click the “add to my favourites” button and it will be available in the “My Favorite” Workout category when you bring it up on your device.

      +

      I’m so special, I can’t find anything I like!

      In the web-based search, you may also notice the “Create a workout” button. When you can’t find what you need, create it!

      For those so inclined, the workout creator is available online and makes for a relatively simple way of creating your own workouts. When you are logged in to Rouvy.com, look for the big “+” in the upper right corner. You can select the “Create a Workout” option from the dropdown menu:

      +

      You will be prompted for the type of workout you want to create (I like “% of FTP%”, as it adapts to the rider’s ability), and then proceed to the editor, where you can name your workout, define the segments/intervals that make up the workout, provide a description, and add workout instructions that are displayed when you want them to show up during the workout.

      +

      You can also choose to make your workouts private for only your own use, or open them up to friends or the public.

      For fun, we created a couple of workouts for you. Just do a search for “Slowtwitch” and you’ll bring up a pair of 2x20 workouts that you can incorporate into your own training.

      +

      Let’s Ride!

      Alright, enough logistics for now - it’s time to execute. Let’s ride!

      Once you’ve selected your workout, the process is pretty straightforward - check your sensors, calibrate your trainer, and get your sweat on! The screenshots below are from the iOS version on an iPad Mini and the Windows version:

      +

      The beauty of Rouvy’s interface is its simplicity. There’s not a lot to do here to distract you from the workout. It’s also a fairly familiar look if you’ve seen similar tools like TrainerRoad or PerfPRO Studio. The main section is the workout graph: the vertical axis shows you your target intensities, the horizontal axis is time.

      +

      You’ll see squiggly lines representing your power, heart rate, and cadence form as you complete your workout. You’ll see your current and average metrics along with a few basic controls that allow you increase or decrease the target intensity if you need to adjust, skip to the next interval, or wrap up your workout early.

      While the majority of the workouts will look like the samples above, there are a few workouts included that have videos (look for the CycleOps 30 minute workouts or the Rouvy Guides). If you’ve got the skills you can incorporate videos into your custom workouts as well.

      Here’s a video workout on the iPad:

      +

      The Aftermath

      Rouvy does a decent job of arming you with the information you need to understanding your last workout, and even see how it fits into your bigger plan. Once you’ve completed your workout, Rouvy provides plenty of data for review and analysis, displayed on a series of panels at the end of the workout, including the GRAPH view and the ZONES & LAPS view.

      All of this information can be uploaded to Training Peaks, Strava, and similar training programs for sharing with your coach or friends.

      Thinking bigger picture, which is really what structured training is all about, we can look at our history through our account on Rouvy.com or in the application. In the application, you can view your history through the “Activities” menu option.

      You can also access your activity history via the “Statistics” menu option when logged in to Rouvy.com:

      There is a lot more to see here - this picture doesn’t do the statistics page any justice. Matter of fact, this article doesn’t really do Rouvy justice. If there’s one thing you should take away from ths series of articles, it’s the realization that Rouvy is a really robust application. It seems that everywhere you look, there more. If there’s one thing that Rouvy could improve on is making it easier to see just how much - how many course, how many workouts, how much information - is tucked away in this application.

      For now, enjoy your workouts in Rouvy and stay tuned - we have a lot more to cover in the near future.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/SMART_Indoor_Training_How_Do_You_Measure_Up__7570.html b/site/slowtwitch.com/www/Indoor_Training/SMART_Indoor_Training_How_Do_You_Measure_Up__7570.html new file mode 100644 index 0000000..0e1f7ed --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/SMART_Indoor_Training_How_Do_You_Measure_Up__7570.html @@ -0,0 +1,649 @@ + + + + +SMART Indoor Training: How Do You Measure Up? - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      SMART Indoor Training: How Do You Measure Up?

      + +
      +
      +
      +

      We love exploring all aspects of indoor training here at Slowtwitch and we’ve spent a lot of time over the last few months enjoying the more fun and entertaining elements of Zwift, Rouvy, The Sufferfest, and others. For the most part, we’ve highlighted some of the “free riding” aspects of these applications - exploring routes, chasing carrots, and just having some fun on the trainer. Now we’re going to change our tone just a little bit.

      You may have heard of SMART goal setting. SMART is an acronym for Specific, Measurable, Attainable, Realistic, and Timely - attributes that all goals need to have. This is the first of a handful of articles we’ve dubbed “SMART Indoor Training”, in which we’re going to pay a bit more attention to all those numbers indoor training applications give us and use them. We’ll delve into how to set goals, establish a baseline, monitor, and structure your indoor training experience in order to achieve your goals. Whether it’s an event, a fitness goal, or something completely different, we need to figure out where you want to be, where you are now, and how you can use structured and well-monitored indoor training to get bridge that gap.

      We’ll start off with a discussion of measurement and how we use different metrics throughout the training and racing season. In subsequent articles, we’ll talk about setting and prioritizing goals. We’ll talk about how to establish a baseline of where you are now by testing. We’re going to talk about how you can use training plans to structure your off-season and early ride season to support your goals. We’ll discuss how you can use tools like TrainingPeaks or Strava to help track your efforts and hold you accountable to yourself and/or to your coach. Much of this will be a discussion of power - how we measure it, how we can use it to shape each workout, and how we can use it to shape an entire season.

      For those using a coach, we’ll cover how you and your coach can work together to use indoor training software such as Zwift, Rouvy, TrainerRoad, and others to build effective and motivating workouts that are appropriate for you and provide feedback for your coach. For those who don’t have a coach, we’ll explore some of the off-the-shelf training plans available through indoor training apps and help you discern which app provides the best solution for your needs and personality. We have a lot to cover, so buckle up and hang on!

      +

      What Gets Measured Gets Managed

      In the Business Intelligence world, we have a saying: what gets measured gets managed. Simply put, in order to understand the health of a business entity, we need to establish useful measurements. I’ve been in the IT industry for almost 25 years, and have seen a lot of efforts to measure performance of companies, teams, software applications, and hardware. Some of them have been useful, but a majority of them seem to miss the mark. Sometimes the intent is good, but the execution is ill-informed. Sometimes they are so far off the mark where they are detrimental and distract from the true objective.

      For example, a Manager might want to measure team member productivity based on how many of X they can produce in a given week. A Software Developer might need to write 200 lines of code per week for a software application. A Tester might need to perform 2 tests per day. A sales person might be expected to sell X number of widgets per month.

      For a sport analogy, if you are trying to measure your training effort, would you simply count the number of workouts you get in during a week? A lot of basic training plans for triathletes will suggest 9 workouts per week - 3 swims, 3 runs, and 3 bike rides. “This was a good week! I worked out 15 times - it’s a new record!” By itself, that doesn’t really mean much, does it?

      When it comes to measurement, these are examples of basic measurements of quantity. These are simple counts. They’re useful, but only in the sense that they can be used as building blocks for more advanced metrics. What we need to do is combine primary measurements (counts, elapsed time, other counts) to extrapolate something more meaningful. From there, we can establish appropriate goals that can be met while factoring in outside influences (i.e., maintaining balance of work, family life, hobbies, and fitness goals). If a Software Developer writes 200 lines of code, but introduces a new bug, is that really success? If I worked out 9 times this week in an effort to do an Ironman in July, is the number of workouts by itself useful? What if that was six 15-minute bike rides, a half-Marathon my buddy talked me into at the last minute, a couple of other runs, and I never hit the pool once? Was that really optimal for an Ironman triathlete?

      The main point here is good measurements need to have good context. Indoor training provides the tools to measure ourselves, set goals, and monitor progress.

      +

      How Do We Measure Ourselves?

      Over time, the exercise science community has tried, tested, and improved upon many approaches to measuring sports performance. In the cycling world, we’ve used metrics such as speed, heart rate, and power to get an idea of how well we’re performing. The challenge with any of these, as standalone metrics, is that there are a lot of outside influences. Good metrics account for these outside influences.

      If you go out and ride 20 miles and average 20 miles per hour on one day, and do that same ride a week later, but average 19 mph, did your performance really drop 5%? What if it was calm and a comfortable temperature the first ride but there were gusty winds or it was considerably warmer on the second ride? Your performance differential has been essentially nullified because of the weather. Heart rate can also vary considerably based on your hydration levels, sleep, stress, caffeine intake, etc. An especially high heart rate during a workout is not necessarily a measurement of your output at that moment - it might show that you’re working hard, but it doesn’t necessarily show that you’re producing. Speed and heart rate, as metrics, have a lot of external influencers.

      Power is a more effective metric, as it demonstrates your actual production - what’s going into the pedal. Regardless of how warm it was, how well hydrated you were, and how much sleep you got the night before, that power - usually measured in watts - is exactly what you could produce for that moment, or that single sample. Continue to sample your power repeatedly, taking several samples per second over seconds, minutes, and hours, and now you continue to improve the context of your measurement and start to see what your true ability is.

      Up until fairly recently, power was a bit harder for us to get - we didn’t have the tools, or the tools were priced out of reach for most. For the rest of us, we could at least get a trainer and spin - it was better than nothing, but it lacked structure and measurement. Over the last decade, this has changed drastically. Power meters, smart trainers, and power virtualization have all developed immensely, with costs coming down and quality going up. Indoor cycling apps have leveraged power and put it to use in a variety of ways. Maybe it’s just a number on our screen as we ride through Watopia in Zwift. Or maybe it’s a target for our current interval in The Sufferfest. It could be coming from your power meter, estimated by your smart trainer, or estimated using your speed sensor and classic trainer using virtual power.

      Power is the primary metric that indoor training applications use, and this can then be augmented with heart rate and cadence to help us draw a picture of our performance. We now have so many ways to leverage power that we put ourselves at a disadvantage if we don’t measure and understand power-based indoor training. Effective use of these tools will not only show up in our race results, but will also prevent burnout and injury, too. With power in mind, we can continue to build your off-season to support your goals.

      +

      Goals are Dreams with a Plan

      Helping you have a fun and effective off-season in order to turn your dream into a goal is the goal of this SMART Indoor Training series. In the next couple of weeks, we’ll be covering several aspects of planning in order to achieve your dreams, with the following articles:
      • Setting Goals - Your “A” Race
      • Establishing a Baseline using Functional Threshold Power (FTP)
      • Using a Training Plan to Monitor and Manage Your Season
      • Holding Yourself Accountable
      Each of these articles will involve measurement, taking us from looking at making the most out of a single pedal stroke to making the most out of an entire season of pedal strokes. To help enforce our concepts, we’re even going to have a test subject: me. Join in as we see how I’m going to achieve my goal for 2020.

      This should be fun.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/SMART_Indoor_Training_Setting_Goals_Your_A_Race__7578.html b/site/slowtwitch.com/www/Indoor_Training/SMART_Indoor_Training_Setting_Goals_Your_A_Race__7578.html new file mode 100644 index 0000000..49eba6c --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/SMART_Indoor_Training_Setting_Goals_Your_A_Race__7578.html @@ -0,0 +1,648 @@ + + + + +SMART Indoor Training: Setting Goals (Your "A" Race) - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      SMART Indoor Training: Setting Goals (Your "A" Race)

      + +
      +
      +
      +

      One of the main problems with the fitness goals that we set for ourselves is that they aren’t goals at all - they are good intentions, but they lack the necessary definition to be real goals. We can define real goals using SMART goal setting.

      SMART stands for “Specific”, “Measurable”, “Attainable”, “Realistic”, and “Timely”. We recently introduced our SMART Indoor Training series of articles, where we’re exploring the ways that modern indoor training applications can be used to support your efforts to attain your goals. Indoor training provides a powerful toolset for any athlete looking to set goals and measure their progress. We started with a discussion specific to the “Measurable” component of SMART. Measurement is fundamental to all aspects of monitoring our progress towards attaining our goals. Without measurement, we can’t define success. We also can’t define success if we don’t have a goal to achieve in the first place, so that’s what we’re going to discuss in this installment.

      +

      The Road to Hell is Paved with Good Intentions

      If someone were to ask you what your goal is, how would you answer? As an endurance athlete, you might respond with an upcoming race or event. You might even refer to it as your “A” race.

      After a bike accident in 2016, I had completely failed at setting any kind of fitness goals. There hasn’t been an “A” race. In late December, I wanted to change that and started jotting down some ideas. Buried in a whiteboard full of goals as a dad, husband, homeowner, business owner, employee, and person with too many interests and not enough time, were a few rough fitness goals:
      • Lose weight
      • Improve body composition
      • Ride a bike every day
      • Ride all of the routes in Zwift
      • Win an indoor race
      • Do yoga again
      • Not hurt as much
      • Do a metric century in another state
      In their rough form, these seemingly random musings need a lot of work to be more than just good intentions. They certainly don’t come close to meeting the SMART goal setting principles. Nonetheless, the purpose of the exercise was to throw down ideas - good intentions - so we have something to transform into real goals. We can use the SMART principles to do this.

      Specific

      If we look at a couple of my goals, they aren’t especially specific. For example, “lose weight” or “improve body composition”. Losing weight is a common New Year’s resolution that often fails because it lacks specificity. Improving body composition complements weight loss, so we have two good intentions that can shape up to form a more realistic goal. But, we still need to ask more specific questions. How much weight? How did we arrive at our specified target weight? What is the time frame?

      An “A” race is a good example of being specific - an event, that is of a certain length, on a certain date. Now we’re talking! Whether it’s a local Sprint, an Ironman, or a gravel ride, the specific nature of an event inherently provides many of the specifics we need and gives us a lot that helps us maintain a contract with ourselves. For example, the act of paying for an event or making the travel arrangements helps us hold ourselves accountable.

      The goals I had jotted down on the whiteboard are way too vague to be meaningful on their own, but they could be refined using SMART principles. We can also look at these goals and start to flag any that might not work, or at least should be a lower priority. For example, losing weight can be difficult for someone training for a metric century because of the energy needs of training for such an event. So, maybe that “lose weight” goal needs to be adjusted - either by putting a specific number on it (which could be zero, if that’s most practical), or the priority can be dropped - let what happens happen, as long as we’re safely losing weight without impacting our race training.

      Measureable

      We talked about measurement in our last installment, and we need to look at each goal and understand how we can apply measurements to them. A metric century has a built-in measurement, but it’s only one of many. A metric century goal means more when we can add another metric to it - perhaps we want to do that metric century in 4 hours or less. If we want to lose weight, we can set a goal - maybe it’s 10 pounds. Once again, that’s a single number, but perhaps it’s 10 pounds over 10 weeks. Every goal needs metrics, or otherwise they are just good intentions.

      Attainable

      Indoor training provides tools to help us understand how attainable our goals actually are. Can win Kona this year? That’s what they work for. Can you win Kona? The Ironman World Championship is certainly attainable by someone, but so is a moon landing. You have to have the means to do it.

      In my brainstorming activity, I included “win an indoor race.” The trick with this one is that what defines an attainable race win has a lot of variables. What kind of race is it? Who else is at the start line? You might know who the competition is in your local triathlon scene, but what about a worldwide virtual race scene? Factoring in what is out of your control and focusing on what you can control will help make your goals more attainable.

      Realistic

      It’s easy to mix the concepts of “attainable” and “realistic”, but all of the SMART principles play off each other nicely. For a goal to be realistic, we need to have an honest look at ourselves - our selected goals in relation to our lifestyle, other goals we’ve set, or in consideration of alternate goals. Attainable suggests that someone can do it, but realistic means that you can do it. Don’t compare yourself to others, and don’t let others’ goals throw yours off.

      I don’t know how many times I’ve heard the phrase, “I just do sprint triathlons - I’m not an Ironman or anything like that” from customers. There isn’t a “just do” sprints - it’s an amazing feat of its own, and when we consider everything else in our lives - family, work, other social obligations, hobbies, etc. - to get out there and hammer out a sprint can be a realistic and very satisfying primary goal.

      Timely

      TIming is everything, and for goal setting, it’s an important factor on multiple levels. A goal can be untimely if it’s too soon and you’re not ready, or it could be untimely if it’s too far out in the future to be practical. Of course, it also has to fit into your overall schedule, not only in terms of a race season, but in terms of how your race season fits the rest of your life.

      Time can be on our side, if we manage it wisely. If our “A” race is months out, there is a lot of value in using small, intermediate goals to help us get there. Whether you are motivated by having some “B” races spaced out on the calendar prior to the “A” race, or even go workout to workout, we can use the calendar to our advantage. Giving yourself smaller weekly or monthly goals and rewards will make the journey more manageable and enjoyable.

      +

      Do Your Goals Scare You? Make them SMART.

      As you can see, the SMART goal-setting principles all work together and play off one another - it’s hard for something to be attainable if it’s not timely, and it’s hard for something to be specific if we don’t determine how we can measure it, and so on. Once you’ve looped through all of these principles a few times for each goal, and again through these through your collection of goals, you’ll see how the goals can complement each other, potentially distract from one another, or be prioritized depending on how they fit into your current place in life and time.

      I encourage you to do this goal setting activity if you haven’t already this year as you plan your season, whether it is outdoors or indoors. Make your list, write it down, make it SMART, revisit it, and hold yourself to it. Make a contract with yourself to achieve something that is meaningful to you.

      The goal of this article, and this series, is to make you think a bit about where you're going and how you can get there. We love modern indoor training, and we’re going to explore more ways to leverage indoor training applications to help you keep your contract with yourself. Our upcoming installments in this SMART series will delve into how you can establish a baseline so you can know where you’re started and using training plans and other tools built into indoor training applications to help you reach your goals in the SMARTest way possible.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/SMART_Indoor_Training_You_Are_Here_7669.html b/site/slowtwitch.com/www/Indoor_Training/SMART_Indoor_Training_You_Are_Here_7669.html new file mode 100644 index 0000000..62d0f91 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/SMART_Indoor_Training_You_Are_Here_7669.html @@ -0,0 +1,652 @@ + + + + +SMART Indoor Training: You Are Here - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      SMART Indoor Training: You Are Here

      + +
      +
      +
      +

      In our first two installments of the SMART Indoor Training series, we discussed how we use measurement as a key element of defining our goals. SMART is an acronym for “Specific”, “Measureable”, “Attainable”, “Realistic”, and “Timely”, and we can leverage these concepts to set and meet our goals. You can catch up on the first two installments here:

      SMART Indoor Training: How Do You Measure Up?
      SMART Indoor Training: Setting Goals (Your "A" Race)

      So far, we’ve started to form a game plan for a successful season - we’ve set our goals and started to think about how we can make goals that adhere to the SMART principles. With goals in mind, we can now start to shift our focus towards the tools that indoor training software platforms provide in order to support our goals. By honing in on the features that are most useful to you, you can settle on the best indoor training applications and hardware for you.

      Today, we’re focused on establishing your baseline performance metric. We can gauge endurance sports performance in simplest terms as our ability to cover a distance over time - how long does it take you to run 5 km? How long does it take you to swim 100 meters? How long does it take for you to ride 40 km? These are measurements of pace that we’ve been doing for years (and don’t require a lot of special equipment), but they don’t tell the entire story. We can enhance those pace measurements by adding in additional biometric indicators such as heart rate, lactate threshold, sweat rate, or V02 max. Was that 20 minute 5k run your absolute best effort? Could you do that every weekend?

      Outdoors, we have external factors - temperature, wind, hydration levels, sleep quality, time of day, road surfaces, traffic, etc. - that introduce variability to the aforementioned pace and biometric measurements and make it harder to get an apples-to-apples comparison of individual performances. Indoor training removes some of those variables - you have more control over an indoor environment that will lend itself to better comparisons over time. The room temperature is more constant, there isn’t any wind, and you can mount your bike in your trainer the same way every time. We can leverage indoor training for a SMARTer baseline - more Specific, more accurately Measurable, and potentially more Realistic.

      Admittedly, we’re going to cater to the cyclists today - the major indoor training applications and hardware on the market have been cyclist-centric, for the most part. By nature, being coupled to a bicycle allows for additional measurement opportunities of a cyclist’s output - we can get power. That’s where we’re going to focus today: How can you measure your cycling power?

      In physics, power is defined as the time-rate of doing work. It is generally measured in average watts over a period of time. In power-based training, we commonly use Functional Threshold Power (FTP), which is the average wattage that a person can sustain for 1 hour. FTP has become a common and popular means for cyclists to benchmark their fitness, as it makes for a relatively simple distillation of one’s output. But first, we need to arrive at your FTP. All of the major indoor cycling applications provide one or more methods of testing a rider’s FTP. FTP tests come in different shapes, so let’s look at our options.

      +

      The Worst 20 Minutes of Your Cycling Life: The 20-Minute FTP Test

      If there is one FTP test that could be described as the standard, it would be the 20-minute test. The construct is simple: warm up well, and then go really hard for 20 minutes. Take the average wattage for that 20 minutes, multiply it by .95, and you have a decent estimate of your FTP.

      If you’ve never done an FTP test before or don’t have a lot of experience with power-based training, you might not know what “go really hard” is. I tend to describe it as “the worst 20 minutes of your cycling life.” This is why I’m not in Marketing... 20 minutes of feeling like your heart is going to explode is awful, especially if you have no idea how to pace yourself. The dichotomy of an FTP test is that you need to kind of have an idea of what your FTP is in order to have a target wattage. If you don’t know your FTP, where do you start?

      For a cyclist who is new to power, a starting point might be to target an FTP equal to your bodyweight in pounds. If you weigh 150 pounds and are of at least average fitness, you could potentially target an FTP of 150. Of course, that would then mean that you would have to shoot for a bit more than that for your target wattage during the 20 minutes of the FTP test. Take your weight, divide by .95 and you’ll have a test target number. 150/.95 is equal to roughly 158. Pacing yourself for 20 minutes is mentally and physically hard and will take some practice. Start easy, and take it a minute at a time. If it doesn’t seem like you nailed it, that’s OK - FTP testing is something you’ll be doing somewhat regularly going forward, and you’ll get better at it.

      Several of the major indoor cycling applications - Zwift, Rouvy, TrainerRoad, and others - offer a 20-minute FTP test built-in, but this is one that you can do on your own, if you have a power meter and a calculator.

      +

      8 Minute FTP Test

      The 20 minute FTP test can be very difficult to pace - riders new to it might go out too hard, while riders who have done it before might go out too easy and surge at the end. Because of this, the 8 Minute FTP test is offered as an alternative.

      The protocol: warm up well, go really hard for 8 minutes, rest briefly, go really hard for another 8 minutes. Your FTP is the average of the two 8-minute blocks multiplied by .9. Compared to the 20-minute test, you are aiming for higher wattages during the two 8-minute intervals, but the shorter duration will be easier to pace. If you feel like you went too hard or not hard enough during the first interval, you get a chance to do better. TrainerRoad is the only major app to offer this option built-in, but you can also do this one manually with a power meter.

      +

      Ramp Test

      Ramp testing has become popular in the last couple of years as a means to get a decent estimate of a rider’s FTP without the pacing challenges of the 20-minute FTP test. It’s meant to be a quicker and less painful test that will be easier to do more regularly.

      The concept is simple: hold a target wattage for a minute, increase the target, hold that for a minute, and repeat until you can’t do it anymore. Once you have your best minute, you can then calculate your FTP by multiplying that by .75. TrainerRoad, Zwift, and The Sufferfest offer ramp tests.

      +

      The Sufferfest’s Take on Power: 4DP

      The Sufferfest hasn’t been mentioned much yet because they have their own take on FTP testing. They have two different FTP tests: a ramp test called the Half Monty, and a more comprehensive exam called Full Frontal. Full Frontal utilizes The Sufferfest’s 4DP concept, which measures more than just FTP - it looks to understand what kind of riding best suits you. Are you a sprinter? A climber? A time-trialer? Full Frontal will help you find out by utilizing (2) 15-second intervals, a 5-minute interval, a 20-minute interval, and a 1-minute interval, all in the span of an hour. Yeah, it’s brutal.

      +

      Dynamic FTP Testing: Just Ride

      Finally, a couple of the major applications - TrainerRoad and Zwift - support a dynamic FTP measurement by simply riding. For example, if you join in a race in Zwift and put in a solid effor for an hour, you might get rewarded with a new suggested FTP. This is the easiest and most natural way to measure, and ideal for beginners who might not know where to start. Just do your thing!

      +

      You Are Here.

      Once you’ve performed your FTP test, you have your number. Your FTP is yours and yours only. If you’re bummed because your FTP isn’t as high as your group ride friend you’re always struggling to keep up with, that’s OK. If you’re perplexed because your FTP is higher than your group ride friend who you’re always struggling to keep up with, that’s OK, too. Group rides introduce a lot of external factors that we’ve stripped out of the equation for now.

      With FTP, we now have a clear metric with which we can work. The next step is to use that number as a baseline for training over the near future. Leveraging training techniques that have been proven through years of practical application will help you make your number grow. We’ll delve into workouts and training plans in the next two installments of this series, looking at these as constructs of short-term and intermediate goals to raise your FTP to support your bigger goals.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Saris_MP1_Platform_7648.html b/site/slowtwitch.com/www/Indoor_Training/Saris_MP1_Platform_7648.html new file mode 100644 index 0000000..b6260aa --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Saris_MP1_Platform_7648.html @@ -0,0 +1,651 @@ + + + + +Saris MP1 Platform - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Saris MP1 Platform

      + +
      +
      +
      +

      You place your smart trainer on this platform, to make that trainer feel more like you’re riding out and on the road. That's the idea. Does it work? Yes. But not in the way I expected it to.

      I thought the sexy part of this trainer was going to be how it reacted when I was out of the saddle. But I didn’t find that a particularly compelling experience. I didn’t dislike it. But it wasn’t a rewarding enough difference to cause me to want to part with $1,200. But I was compelled by something else this platform did.

      +

      What I didn’t think would matter much was the experience of riding while seated. But this is where I did find a difference.

      At least that's what I thought I was feeling. I didn't want to imagine or talk myself into anything. So, I set up another stationary workstation, same rider position, same pedals, so that I could hop off one and onto the other and get instant feedback.

      This is what I did, over a number of days. Switching bikes, on and off the MP1 Platform, back and forth. Folks I know on Zwift were messaging me. “You’re testing something, aren’t you?” I’d be in the middle of a Zwift ride, stop, start a new ride because I had to reboot Zwift each time I switched bikes.

      As to the presentation of the platform itself...

      +

      The Saris MP1 Platform shows up packaged perfectly – if you get it via mail order – and the assembly was easy and quick. One thing. I couldn’t get past the creaking, at the edge of the platform when it moved side-to-side. I asked Saris’s man-for-all-seasons, Brian Turany. “Another user complained of this,” he said, but only that one. I lubricated, I inspected, nothing worked. So I started taking pictures, and sent this one below to Brian.

      +

      You’re supposed to remove those pads between the wheel and the leaf spring, he told me. Those pads are for shipping. I took the pads out. No more creaking! Companies often have to update their user instructions after I review a product, because I can find a way to mess something up beyond the capacity of a normal person.

      I videoed myself on the platform because there’s really no good way to describe it. You have to see the platform in action to get the gist of it.

      +

      +

      The upshot is this: I like the platform. I would definitely find an improvement in my cycling experience with the platform, if we’re talking about a smart trainer rather than a smart bike.

      However, as noted, the platform pleasantly surprised me in an unexpected way, but what I thought the platform would provide me is not a feature I’ve so far found compelling in the way I expected. Why? Two possibilities. First: I have not been riding outdoors this season at all, and because it always takes me awhile to get used to riding out of the saddle once outdoors, maybe it’s just that. Second, perhaps it’s that lack of rotation about the steering axis that I miss.

      +

      A note about the images here. You’ll see a couple of risers that come with the platform, and they’re installed one on the other, like Legos, as you need more and more rise. You’d need that rise if you’re riding a smaller front wheel.

      You might note that one footpod on the H3 trainer is near the edge of the platform. This is because trainers aren’t symmetrical, side-to-side. The cassette body sits out one side. When you center a trainer on the platform, one leg may sit out farther aside than the other, and that’s the case with the H3. Also, the H3 has particularly “long legs.” More so than other trainers. But the platform is perfectly stable. The bike is absolutely locked down on the platform in front, and the trainer likewise in back. Nothing is going anywhere on this platform.

      +

      The images here show the underside and what you’re looking at are the mechanics that make the platform move. There’s a set of curved tubes front and rear and the trainer moves on these tubes via wheels. This is the fore/aft movement you sense when you’re getting in and out of the saddle and the video embedded here clearly shows this.

      Then there are leaf springs, and this is what allows the trainer to move from side to side.

      +

      Would I buy this platform? f I had a good smart trainer, the MP1 platform is definitely an enhancement. Yes, I would buy this platform. But if I had to choose between a smart bike – the KICKR Bike or the NEO Bike – I’d pick the smart bike as my purchase (and those bikes won't work on this platform). But, these smart bikes cost between $3,100 and $3,500 versus $1,200 for the platform. Also, you can’t put a KICKR Climb on this platform. Both Wahoo and Saris say no can do. Mind, I’m tempted anyway. But I’m not going to try it.

      In my opinion, if you’re a Zwift racer, and you think the MP1 Platform is going to give you the edge when it’s money time and you’re sprinting for dough, either the answer is no, this isn’t the device that’s going to get you the extra 10 watts through ergonomics, or the answer is maybe you might get those watts and I just am not a good enough sprinter to know whereof I speak.

      If you’re a just-riding-along kind of person, seated almost always, looking for a more road-like experience in subtle ways, they yes. This device delivers that for sure.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Slowtwitch_12_Days_of_Xmas_Feedback_Sports_Omnium__7504.html b/site/slowtwitch.com/www/Indoor_Training/Slowtwitch_12_Days_of_Xmas_Feedback_Sports_Omnium__7504.html new file mode 100644 index 0000000..e276b1a --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Slowtwitch_12_Days_of_Xmas_Feedback_Sports_Omnium__7504.html @@ -0,0 +1,656 @@ + + + + +Slowtwitch 12 Days of Xmas, Feedback Sports Omnium - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Slowtwitch 12 Days of Xmas, Feedback Sports Omnium

      + +
      +
      +
      +

      Between Friday, November 29th and Monday, December 16th we will highlight a new product each week day as part of our Slowtwitch 12 Days of Xmas Gift Guide.

      Without further ado, on the first day of Xmas we present Feedback Sports Omnium Bike Trainer.

      +

      + +

      + +

      The Omnium is the ideal trainer for athletes on the go. It’s been our experience that lugging around a large trainer when driving or flying is cumbersome, exhausting and expensive. The Omnium changes all that. It weighs in at just 14lbs, easily folds up and fits in it’s own padded bag.

      +

      + +

      +

      This little companion allows athletes of all levels to get in your bike workouts anywhere you choose and adds a new element to the ease of warming up before your next event. It’s portability makes it ideal for workouts in your hotel, AirBnB, or outside of transition before the event.

      +

      + +

      +

      The adjustable fork mount secures the front of the bike and allows the rear wheel to move from side to side giving a roadlike feel to any ride and their internal progressive resistance design means it can handle any effort you throw down.

      +

      + +

      +

      Back when she was a globetrotting triathlete, this was Gwen’s trainer of choice, and we know of a few champs on the long course circuit who use it too.

      $429.99, Feedback Sports

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Slowtwitch_12_Days_of_Xmas_Tacx_NEO_2T_7528.html b/site/slowtwitch.com/www/Indoor_Training/Slowtwitch_12_Days_of_Xmas_Tacx_NEO_2T_7528.html new file mode 100644 index 0000000..1601cad --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Slowtwitch_12_Days_of_Xmas_Tacx_NEO_2T_7528.html @@ -0,0 +1,666 @@ + + + + +Slowtwitch 12 Days of Xmas, Tacx NEO 2T - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Slowtwitch 12 Days of Xmas, Tacx NEO 2T

      + +
      +
      +
      +

      Between Friday, November 29th and Monday, December 16th we will highlight a new product each week day as part of our Slowtwitch 12 Days of Xmas Gift Guide.

      Now on the seventh day of Xmas we present Tacx NEO 2T.

      +

      + +

      + +

      This is Tacx’s third generation of their top-of-the-line NEO, the NEO 2T. The NEO line has always offered some unique features that help it stand out from the pack, including simulation of road surfaces such as cobblestones or gravel, built-in pedal stroke analysis, and is the only trainer on the market that will actually simulate a descent.

      +

      + +

      +

      On the ascent, it will simulate up to a 25% grade, and can produce a maximum of 2500 watts of resistance.

      +

      + +

      +

      In terms of accuracy, the NEO 2T is amongst the top performers in that category with a +- 1% rating even though you’ll never need to calibrate it.

      +

      + +

      +

      It comes with a front wheel block, quick releases for your road and off-road bikes and is compatible with all the main players Zwift, TrainerRoad, Sufferfest and Rouvy.

      +

      $1399, Tacx NEO 2T

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Slowtwitch_s_Pinball_Wizard_6780.html b/site/slowtwitch.com/www/Indoor_Training/Slowtwitch_s_Pinball_Wizard_6780.html new file mode 100644 index 0000000..1519c0e --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Slowtwitch_s_Pinball_Wizard_6780.html @@ -0,0 +1,617 @@ + + + + +Slowtwitch's Pinball Wizard - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Slowtwitch's Pinball Wizard

      + +
      +
      +
      +

      Just when I scratched out a mental toehold on Zwift I find out there’s a whole world of racing unknown to me. One such event – a World Cup no less – took place at the L.A. this weekend, featuring well north of $100,000 in prizes, mostly cash.

      Slowtwitch favorite Lionel Sanders was there, in town for weekend-after-next's Oceanside 70.3, and he took part because, well, that’s Lionel. He likes to have fun, and doesn’t take schedules and taper routines over-seriously.

      +

      The race was at the L.A. Velodrome but I doubt Lionel ever circled it once. In fact, for his several races over 2 days he never dismounted his trainer.

      Lionel (below left) found out about this event through Slowtwitcher Matt Gardiner, (below right) who is TriowaCPA on our reader forum. He's another of the 20 men (along with 20 women) entered in the event.

      +

      Matt is a full time CPA by trade, and a 70.3 racer by hobby. He isn’t a bike racer. What does he do well? Two things: first, your taxes; second, put him on a trainer, in front of a computer, and he’s a pinball wizard.

      "Zwift isn’t the same as racing outdoors,” Matt said. Pure power can win races in this game unlike outside where some skills and tactics matter much more."

      In fact, let me stop here. Just watch a few seconds of video below.



      This race was disorienting to “spectate” because I’m watching a race on the screen. A real race. Well, a real virtual race. And then the camera would pan out to the actual action, that is, the riders on their trainers. It felt very… Matrix. Or, Pacific Rim. Which was real? The avatars on the computer were real. The actual people pedaling in place were not as real. That was my takeaway, from in front of my computer, in my peejays.

      +

      One very real thing that was the check Matt was handed. Okay, that check wasn’t real, but was a proxy for one that was. What a world!

      This event was put on by Cycligent and everyone is allowed to race who has a bike, a trainer, a power meter, and Zwift. Cycligent has – what? leveraged? appropriated? hijacked? – the Zwift racing platform and calendar for its own overlay. It’s the ASO of Zwift. Riders must register for the CVR League and connect to Strava for race results.

      +

      Once registered and connected, riders select one of 8 time zones to race in on Tuesdays, and are assigned to a bracket based on ability. The brackets are based on race history in Zwift, but if you’re an “A” rider (FTP of 4+ w/kg) you can be asked to be assigned to that bracket. Other brackets compete just the same, but winning in the B, C, or D bracket won’t earn you a place in the live event—just bragging rights.

      For Matt to get to the Los Angeles event he had to accumulate the most points in his Zone (8 tota races, 6 highest count). Each Zone winner was invited to race in the live event. Cycligent offered guest slots, Lionel Sanders getting one.

      +

      Grand prize for men and women was $7,500 base but was increased by crowdfunding. Minimum prize was a base of $1,250. Online spectators could “cheer” for their racers by sending them money. It would show up in real time on the racer’s screen. Take that, Peter Sagan! Fifty-percent went straight to the racer, 25% to the prize pool, and 25% to Cycligent.

      "It was absolutely incredible to see the support from my family and friends during the race,” Matt said. “Overwhelming!”

      +

      Lional Sanders and Matt Gardiner went swimming Sunday before the event. "He did 4K of drills and form work,” said Matt, "I did 1.8k to stretch out the body – I’m not stupid enough to try to keep up with him in the pool or swim a full hour before the hardest bike race of my life!” Here’s another great video of Lionel’s weekend:



      After the “optional” morning swim there were 4 heats to decide who would race in the Elite bracket and who would race in the Performance bracket on Sunday. The top 4 racers from each heat of 10 qualified for Elite bracket, then the next 2 fastest times also qualified, rounding out 10 Elites and 10 Performance riders.

      +

      Matt was pitted against Lionel in the qualifier and they split the pack with one other very strong rider over the top of the first climb. The three gapped the field by 3 minutes, and Matt soloed to take the overall win in that race.

      +

      On Sunday, there were 3 races: Mountain Climb, Hilly Road Race, and Criterium. Matt placed 2nd on the mountain climb behind Lionel, then he placed 5th and 9th in the Road Race and Criterium respectively. Both of those ended in bunch sprints, "which are not my cup of tea.”

      Matt ended weekend in 6th overall, "which far exceeded my expectations. What an incredible experience.” Signing up for the CVR League was, "seriously the best decision I’ve made in sport."

      Lionel then ran 5 x 2k after Sunday’s event. One hell of a training day. He and Matt swam again on Monday for a half hour. "Sanders is world class in attitude and performance,” said Matt.

      +

      I watched the women’s Elite Final. The races were short, 20 or 30 minutes, and were incredibly high intensity. And just when one was over, after barely time to stretch one’s legs, another “stage” began.

      The race was won by Carey Conabeare, a horticulturist from the UK. Is she that good on the road? Who knows? Not me! Virtual bike racing is its own sport.

      Lionel Sanders, as noted, is racing in the Ironman 70.3 Oceanside 2 weeks after his weekend of Zwift racing. Oceanside has a $50,000 prize purse. His race is anticipated by his sponsors, HED, Louis Garneau, Infinit Nutritional and others. He did the CVR World Cup race as an interesting distraction.

      On the other hand Powertap, another Sanders sponsor, was heavily involved with this past weekend’s race, along with its CycleOps sister brand. Garneau was a prime sponsor of the CVR World Cup. And, the prize money here was twice the money up for grabs next week. So, which in the end will have earned Lionel the greater reward? We will see. What a world!

      Meanwhile, TriowaCPA sure… plays… a mean… pin… ball!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Slowtwitchers_Prefer_Direct_Drive_Trainers_7120.html b/site/slowtwitch.com/www/Indoor_Training/Slowtwitchers_Prefer_Direct_Drive_Trainers_7120.html new file mode 100644 index 0000000..7d52bd4 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Slowtwitchers_Prefer_Direct_Drive_Trainers_7120.html @@ -0,0 +1,648 @@ + + + + +Slowtwitchers Prefer Direct Drive Trainers - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Slowtwitchers Prefer Direct Drive Trainers

      + +
      +
      +
      +

      A poll of Slowtwitchers just concluded shows the stationary trainer market is shifting from wheel-on to direct drive.

      The poll asked, "Are you now riding either a direct drive or wheel-on trainer? Are you imminently (this season) planning to buy one or the other?"

      The poll used language identical to a poll taken on Slowtwitch two years ago, with this difference: The poll two years ago asked whether there was interest in a treadmill-style bike trainer; that option returned scant interest then and was omitted from the poll just concluded.

      +

      The 2018/19 poll had 953 respondents, to 802 in the poll taken during the 2016/17 North American stationary season. What changed between then and now?

      +

      Two years ago, 63 percent of Slowtwitchers were riding wheel-on trainers, and that percentage has dropped to 45 percent during this 2-year period. Those riders have moved to direct drive trainers, and the percentage of direct drive user has jumped from 23 percent to 44 percent. The current use of direct drive versus wheel-on is roughly even, though the trend is clear.

      +

      The percent of increase in direct drive trainer use is higher than the decrease in wheel-on use: 21 percent of Slowtwitchers moved from something to direct drive, while 18 percent of Slowtwitchers moved from wheel-on to something else. That extra 3 percent who've moved to direct drive appear to have come from those riding rollers. The use of rollers among Slowtwitchers has been halved in the last 2 years, from 6 percent to 3 percent.

      +

      The poll asked whether Slowtwitchers intend to purchase a new trainer. Only 3 total respondents out of 923 said they're getting rollers this season, and only 5 of those who responded said they're getting a wheel-on trainer.

      But Slowtwitchers may under-report their actual buying behavior. Yes, 2 years ago many Slowtwitchers already made their trainer purchase (the poll took place halfway through the stationary season). And, many made their purchases in the subsequent season. That established, 2 percent indicated that they were prepared to purchase a direct drive trainer during the 16/17 season, and between then and now 21 percent of all Slowtwitchers taking the poll did make that purchase.

      +

      In 2010 Slowtwitch polled its readers, asking which stationary trainer device was their favorite. Kurt Kinetic sat at 13 percent, CycleOps at 9 percent. There was no Wahoo Kickr back then. Rollers were at 7 percent, Tacx at 6 percent. "Other" was chosen by 5 percent or respondents. Computrainer has selected by 52 percent of Slowtwitchers. How times have changed.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Slowtwitchers_Take_On_Sufferlandria_6742.html b/site/slowtwitch.com/www/Indoor_Training/Slowtwitchers_Take_On_Sufferlandria_6742.html new file mode 100644 index 0000000..86a81ab --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Slowtwitchers_Take_On_Sufferlandria_6742.html @@ -0,0 +1,642 @@ + + + + +Slowtwitchers Take On Sufferlandria - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Slowtwitchers Take On Sufferlandria

      + +
      +
      +
      +

      Slowtwitch had a solid contingent of readers who tackled The Sufferfest's Tour of Sufferlandria challenge and when prompted to share their experiences from the virtual tour, 14 riders obliged.

      In Part One I recounted my experience of completing 97.5% of the tour. Now in Part Two we outline the experiences of other Slowtwitchers who participated.

      +

      + +

      + +

      KPPOLICH kicked off the 2018 Tour of Sufferlandria forum thread on January 3rd off-the-heels of the tour's route announcement and it didn't take long for others to chime in. TRIDEVILDOG listed out all the stages and included values like Training Peaks' Training Stress Score (TSS) and Intensity Factor (IF) leading to the determination this would be an arduous week in the saddle.

      Following the final stage, Sufferfest's infamous Kitchen Sink - a mash up compilation of various videos - I asked for some input from those Slowtwitchers who took part in the challenge. Here is a breakdown of common answers with quote or two:

      +

      + +

      +

      What was your motivation for doing the ToS?

      "Cycling has been my focus this winter so implementing a cycling block made sense to me and my coach. Plus, the community is very positive and engaging. It is easy to keep pushing as you have hundreds of people posting on the Facebook page with messages, videos and photos." - Brandes

      * Ramp up cycling volume, maintain enthusiasm to be ready for the upcoming season. - Russ
      * Jump start fitness following the holidays. Fun challenge. It's for Parkinson's Disease. - Giorgtd
      * See if I could finish - Gmehje
      * Seemed like a fun way to kick off my cycling block - Oscaro
      * Sounded interesting, fun challenge, great cause. - Lenny07
      * Enjoy the community and the Chronic Training Load (CTL) boost. - Johnnybike
      * Figured the 9 days of ToS could be a great mental test in addition to being a physical challenge. - Northy
      * Mix things up and offset the repetition and boredom. - MSM7182
      * Kick my butt while supporting a great cause. - IRONCHEESE
      * First year cyclist wanting to take my cycling to a new level and slay a dragon. - JFCHARLAND

      +

      + +

      +


      Did you do The Sufferfest's 4-Dimensional Power (4DP) test prior to the start?

      "Updated 4DP 1 week before ToS, 13-16% increases for the 4 metrics. Next planned 4DP is April 14th as I had planned on testing every 8-10 weeks, may do one sooner as I think ToS definitely boosted fitness more than expected and I think I would see an immediate increase across the board." - Lenny07

      * Yes (x8)
      * No (x6)



      For further clarification on The Sufferfest's 4DP test (which has been hotly debated on the ST Forum) APEX Coaching's Neal Henderson explains his creation in this short video above.

      +

      + +

      +

      What did your setup include (type of trainer, power meter, etc)?

      "Tacx Vortex Smart over FE-C using an ANT+ USB stick in a Win 8.1 laptop. Also recorded data on a Garmin 920XT with power from Vector 3 pedals." GIORGITD

      * CycleOps Hammer (x2)
      * Kurt Kinetic with virtual power (x2)
      * Wahoo KICKR Snap (x2)
      * Wattbike (2nd generation)
      * CycleOps Fluid 2 with Vector 3 pedals
      * Wahoo KICKR with PowerTap P1 pedals
      * Wahoo KICKR
      * Blackburn Ultra trainer with Power2max
      * Elite fluid unit with Quarq Dzero
      * "Cheap Performance brand fluid trainer that I paid less than $100 for and a wireless PowerTap hub that gets swapped between all of my bikes. Budget setup." -KBD

      +

      + +

      +

      Do you feel like a stronger cyclist at the end of the Tour of Sufferlandria then you were at the start?

      "Of course immediately after completing the 9th stage I did not feel stronger, I felt like I had been hit by a truck. But I know from experience that I will feel significantly stronger in 5-9 days." - RRUTIS

      * "Legs hammered, but feel stronger at the end of ToS. Dropped some weight, a big plus." - Russ
      * "Days are well placed so not everyday is a smash session. First three days my legs were very tired but final three days could feel fitness raising." - Brandes
      * Too tired to think about it. - GMEHJE
      * "Hard to say when I currently am pretty fatigued, but I recon this will help both wattage wise and with the extra mental push when tired." - Oscaro
      * "Absolutely. Amazingly, in fact." - Lenny07
      * "Absolutely. Good training block." - Johnnybike
      * "I feel like I have gained as much mental as physical strength." - Northy
      * "My coach didn't want me to do the ToS but I figured this is a hobby and cycling is by far my weakest sport so why not change it up and have some fun with this challenge." MSM7182
      * Of course though will need a few days off." - IRONCHEESE
      * " I had originally planned to keep running and do the ToS at 80% intensity but went 100% nuclear the whole way. Couldn't believe I could suffer like that." - JFCHARLAND
      * "As of this minute, no. But i think this was the toughest training block I've ever done on a bike and I've been riding over 30 years." - TriDevilDog

      +

      + +

      +

      Do you do a significant portion of your triathlon cycling training utilizing indoor platforms?

      * "100% of training since November has been indoors. 90-95% Sufferfest, the rest on Zwift. Will introduce one outdoor ride again once it gets warm but only for bike feel and handling. I much prefer indoor training for focus and safety." - Lenny07
      * "Over the winter almost all my bike training is indoors on the trainer with TrainerRoad. During the season I ride once or twice a week, still using Trainer Road, mostly focusing on intervals that would be hard to set up outside." - Northy
      * "70% of my annual mileage is indoors. Much safer and it just suits my headspace more." - Johnnybike
      * "I would say one-third of my sessions are Sufferfest videos, one-third are from my coach Simon Bennett with Apex and remaining Z2/recovery rides are done on Zwift." - Brandes
      * "I tend to do TrainerRoad more than Sufferfest. Trainer workouts form a big part of my training. I've been on TrainerRoad since it began and I like the structure." -Russ
      * "I really transitioned to indoor training year round. With the exception of races I was only on the bike outside 10 times in 2017." - GIORGITD
      * "I do most of my hard intervals year round on Sufferfest as I find it's easier to push hard compared to being outdoors." -Oscaro
      * "I'm almost 100% riding indoors on my KICKR. Started 10 years ago with Sufferfest videos, moved to TrainerRoad, and now onto Zwift." - MSM7182
      * "I am Zwifting 4-5 times weekly, much more interesting right now." - IRONCHEESE
      * "I do the vast majority of my bike training indoors. It's safer and I get better results with focused indoor training." - KBD
      * "Almost all on Sufferfest but some on TrainerRoad and a tiny bit on Zwift." - JFCHARLAND
      * "During the winter about half of my training involves Sufferfest, during the outdoor riding season that drops to once every 10-14 days." - RRUTIS
      * "Indoor training is pretty much a winter stage of my training although I keep things handy because I will do a trainer ride now and then when schedules make a regular ride inconvenient." - TriDevilDog

      +

      + +

      +

      The Sufferfest launched the annual Tour of Sufferlandria in 2013 and their fundraising component, including a minimum of $10 per person for an official entry, has raised over $600,000 for the Davis Phinney Foundation to assist those living with Parkinson's Disease.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Smart_Bike_Shootout_7622.html b/site/slowtwitch.com/www/Indoor_Training/Smart_Bike_Shootout_7622.html new file mode 100644 index 0000000..b206090 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Smart_Bike_Shootout_7622.html @@ -0,0 +1,649 @@ + + + + +Smart Bike Shootout - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Smart Bike Shootout

      + +
      +
      +
      +

      Today I’m comparing what, thus far in these early months, are the two giant products of 2020: The Tacx NEO Bike and the Wahoo KICKR Bike. The ordering of this shootout is a little backwards because I’ve written my long-term review of the KICKR Bike, but not yet of the NEO Bike. Nevertheless, I know enough about both bikes to compare them. Of the NEO Bike I’ll write in more detail later.

      In most other years these two items might not be the finalists for product of the year. But if there is something this year that eclipses toilet paper as the preeminent need in my household, it’s one of these bikes. I can’t speak for what the Average Joe considers more important but, as for myself, a smart bike and a garden hose and I’m good.

      +

      I’m not including the Wattbike or the Stages bike or the Peloton or any other bike in this shootout, because I don’t have those bikes. I will write about those bikes. (Well, some of them.) The KICKR and NEO bikes are extrapolations each company's premier smart trainer unit, each of which is world leading, and that’s why I’ve started with this particular pair of smart bikes.

      Why a Smart Bike?


      You might ask why a smart bike at all? We live in the most economically perilous time of my life (and I was born in the 50s, so I’ve lived through a few). One reason is because we’re housebound, and you’re going to drive yourself and those around you crazy unless you can find something with which to occupy yourself.

      But let me answer this is in technical terms. There are 2 reasons a bike like this towers over a smart trainer alone for stationary use: 1) Customizable gearing, the capacity to change that gearing, and shifting that never fails; 2) Instant adjustments, in any plane, handlebars and saddle, usually possible during a ride.

      +

      The first thing is really neat. Really neat. The second thing is beyond neat. In years past I’ve written about a luxury I have that others don’t: Because we have a bike fit school on The Compound I have a studio full of easily adjustable fit bikes. For the past 20 years this studio has known better and better fit bikes, and privately, selfishly, as I ride my stationary workouts aboard these fit bikes, I get to make alterations in my fit coordinates during these rides. I dial in my coordinates as I ride. Instant feedback. Now, with a bike like this, you get to enjoy that too.

      Let’s talk these bikes across a half-dozen or so major categories, and I’ve listed what bike I think “wins” each category.

      Display & Configuration


      Ease of Configuration: I configured each of these bikes through an app on a handheld device. My handheld is an iPhone, and on the screenshot below you can see the two apps I used. This can be confusing to someone in my age bracket, when there are multiple Tacx and Wahoo apps. In the case of Wahoo, no, it’s not the ELEMNT app, and it’s not the Tacx Training app, nor the Garmin Connect app. You may well use these other Garmin/Tacx apps when you ride on the Tacx bike, but the Tacx Utility app is what you use when you set these bikes up (settling on your gearing, for example.) Both bikes apps worked fine. I guess I’d give the slight edge here to Wahoo, and maybe it’s me, but sometimes (and in this case) I don’t seem to get things to pair right away with Garmin/Tacx, or the apps aren’t quite as intuitive as they could be. But this is a “soft” plus to Wahoo. Both worked fine.

      +

      Gear display: Each of these bikes lets you visually know what gear you’re in. Why? Because if you’re on your bike on a trainer, you can look down to see what gear you’re in. Not so here, because the gearing is virtual. There are no actual gears on either of these bikes. And I promise you, as you’re on a 9 percent grade in Zwift, approaching what will become a 16 percent grade, you will look down to see how many more cogs you have left. Hence the gear display.

      In this case I give the soft-plus to Tacx, because when I’m riding hard all the blood leaves my brain and goes to my legs and I can’t do 54 divided by 9. (Mind, the older I get the harder that gets, on the bike or off.) When you’re riding hard, and Wahoo tells you that you’re in 1 and 9, what was 1 again? Big ring or small? And, is that the 9th hardest or easiest gear? The longer you ride the KICKR Bike the more you come to recognize and remember that the lower number is always the easiest gear. So, 1 and 9 is the small chain ring and the 9th easiest gear. When you’re on that 9 percent grade and you see a 1 and 4 you know you’re in the small ring and you have 3 more cogs left before you’re out of gear.

      +

      Visibility of screen: This is another soft-plus to Tacx, and you might thing I’m discussing the same feature as I just discussed above. I’m parsing between the ease of seeing the screen, here, with the ease of understanding what the screen is communicating, above. The Tacx screen is right there, no missing it, easy to see. The Tacx bike’s front display, and its fans, may present an obstruction to a big screen in certain configs and I’ll talk about that below.

      Can Take Aerobars: Both these bikes accept aerobars. No issue here. The one minus to these smart bikes is that I can’t imagine any method by which you can shift at the bar-ends. I hope someone will correct me if I’m wrong about this. This is pass/fail, both bikes pass, neither bike wins this.

      Shifting


      Shifter Ergonomics: How comfortable are the shifters? And I mean, the hoods, the hood shape. I have zero problem with either. They’re both very comfortable. Different, but comfortable. I truly do not have a preference. I guess maybe I’d give a slight nod to the shifters on the NEO Bike, which are really comfortable! But that nod is at the expense of some other imperatives, which are listed below. Soft-plus to Tacx.

      +

      Shift Intuitiveness: The NEO display is more obvious than the KICKR display, per what I wrote above. Conversely, the KICKR shifting – what your hands do – is more obvious than the NEO shifting. There are 2 shift paddles on the KICKR Bike’s levers, mimicking Shimano. You can set them up, in the Wahoo app, to act like Shimano shifters, or like SRAM shifters. Because SRAM has only 1 paddle on its levers, both paddles on a shifter perform the same function. You can pretty much configure the shifters to work however you want. This is a medium-big deal. (I posted some screenshots below that shows some of the set-up process in the Wahoo iPhone app.) The NEO's shifting buttons are fine. The left buttons shift the FD, the right shift the RD. They're fine. Just, not quite as intuitive. Plus to Wahoo.

      Shift Mimics Road Riding: I wrote above that the NEO's shifters might be slightly more comfortable than the KICKR’s shifters, and if so, it’s not by much. Both are quite comfortable. But remember, Tacx is making a shifter for a stationary bike. If you hit a bump on a descent on the NEO Bike you aren’t going to do a keester-over-teakettle faceplant on the road. Shimano and SRAM have to take that into consideration when they design shifters. If you ride a KICKR bike you’re riding a true, road style shifter. Is that important to you? Soft-plus to Wahoo.

      +

      Adjustability


      Range of Adjustability: Both are highly adjustable. The adjustment scheme on the Tacx is more straightforward. The Wahoo has taken pains to be a more x/y bike than the Tacx, and a more compact bike. I note that. This allows the bike to be a little lighter, which the Wahoo bike is. But, both bikes are about equal in their range of adjustment. They won’t fit everyone. I calculated that the Tacx bike’s saddle, BB to saddle-top, goes down to about 63.5cm, and that’s with the saddle that comes with the bike, which has a rails-to-top dimension of 45mm. If you put a lower profile saddle on there, like a Specialized Romin, that has a 30mm height profile, you could get down to about 62cm. The KICKR Bike likewise gets the saddle down to about 62cm or 63cm of height.

      +

      Both bikes will accommodate tall riders as well. At least in terms of saddle height. The KICKR Bike will get you a saddle height of about 88cm, the Tacx NEO to 93cm. I’m 6’2” and my road bike saddle height is 76cm. If I were a leggy 6’2” it might be 80cm. So you see, no issue there. Oddly, though, both bikes suffer from a lack of handlebar length that corresponds to saddles that can go up that high. HX is the raw horizontal length from bottom bracket to the handlebar clamp (where the handlebar passes through the stem). In each case, the bikes struggle when you get past an HX of 50cm. I ride with an HX of 50cm, so, I’d be at my limit, except I ride with a shorter reach handlebar that the bar each of these bikes come with, so, I have some wiggle room.

      Just know that if you ride with a “long and low” road position, and you’re over 6 feet tall, you begin to run into adjustment issues with both bikes in this particular metric: handlebar length (i.e., “frame reach”, i.e., top tube length). Both bikes are, in all other cases, highly adjustable as per the typical contact points. The Wahoo bike uses a standard stem, so, beyond all the other adjustment options, if you found you maxed that bike out, lengthwise, in either direction, you could also change the stem.

      Adjustability on the fly: As noted in my intro to this, one of the two major advantages to these bikes is the capacity to adjust them easily, so that you and I can fiddle with our positions. Best is when you can adjust the bike while you’re on the bike. While riding. In the middle of a ride. I’ve made on-the-fly adjustments while riding with both bikes.

      The NEO Bike gives you two options for adjustment of its major coordinates: saddle height and fore/aft, and handlebar height and fore/aft. You can use a set screw, or you can replace a set screw with a handle. Do the latter. Use the handles. If you do, then the NEO Bike is just a tad more easily adjustable on-the-fly than the KICKR Bike. Both bikes' adjustment schemes are in the image above. In that image, see how on the KICKR bike you turn in-line handles 90 degrees, which releases pressure on the telescoping tubes, allowing you to move those tubes in and out. Mind, the KICKR Bike is quite adjustable. But the telescoping features, like the extendable front end, is a little sticky when you try to do this on-the-fly. It can be done. Just, the NEO Bike is a little easier to wrangle. Plus to Tacx.

      +

      Saddle tilt adjustment: Both bikes make it easy to adjust saddle tilt. In neither case can this adjustment be made on-the-fly. Both these brands missed an opportunity here, in my view. However, in each case the bikes are subject to eventual retrofit. Note-to-bike-makers: the more you fart around with a 4mm or 5mm Allen bolt, the sooner that Allen bolt head rounds. The more you fart around with a T20 Torx bolt (as the NEO uses) the sooner that Torx bolt will round. Best to use other methods when possible. Above is the KICKR Bike’s seat post hardware, typical hardware, and next to it the Purely Custom Fit Bike’s seat post hardware. They work identically. If you replace the Allen bolts with thumb screws, you can adjust saddle tile on-the-fly, as you're in the middle of a ride.

      +

      Adjustable Cranks: I’m an inventor-manufacturer in a former life, and the first rule among people who do what I did is: Don’t invent what you can more easily "appropriate". I’ve used every kind of adjustable crank there is, mostly in the context of adjustable fit bikes. Wahoo appropriated what I believe remains the easiest, cleanest adjustable crank, designed by Jeff Keller of Sunrise Cyclery 2 decades ago (inset into the picture above of the Wahoo KICKR crank. You just have a bunch of holes in a crank arm. Choose, in 2.5mm increments down to 165mm.

      +

      The NEO Bike has inserts, each of which corresponds to a crank length. The NEO Bike’s method gives you a cleaner looking crankarm, but the problem is that you have to keep track of the parts you don’t use (such as the parts in the image above). The NEO Bike is also adjustable from 170mm to 172.5mm to 175mm, not down to 165mm. Plus to Wahoo.

      Road Feel


      Smoothness: Both bikes are at the top-of-the-heap when it comes to ride feel. Each of these smart bikes is better than other smart trainers in a number of categories, and ride feel is among them. They're each superior to anything else I've ridden in shifting, in coasting, and both bikes maintain speed during Zwift descents. The NEO Bike can sometimes get ahead of my pedaling, so that I have that load-free spot as my pedaling catches up (if I’m not pedaling well). I hate that. But, I hate that in me, not in the bike. The NEO bike precisely replicates when I’m pedaling badly.

      Maybe the NEO Bike is slightly smoother than the KICKR Bike. It’s hard for me to pick a winner here. They're both just so much better than anything else.

      +

      Road Surface Feel: This is one of these categories where you either have it or you don’t and NEO Bike has it. If you look at this display, above in the right hand screenshot, of road surface features, and how Tacx handles this, you not only get road feel, and you not only get road feel broken down into various road features, you get to choose how ardently you feel each feature. Tacx is rubbing it in here. It knows it has something no other trainer has – whether the NEO standalone trainer or the NEO Bike – and it’s accentuating the lead it’s got on the other smart trainer makers. I would do the same thing. Just keep coming back to what you do best, and pound it home. This feature works on Tacx Films, Tacx Training software and Zwift (I'm awaiting a list of other platforms on which this feature works). Plus to Tacx.

      +

      Pitch Change: And here is the KICKR Bike’s big feature not matched by other trainers. As the road pitches up in Zwift. The bike pitches up. Ooh baby. And on descents, it pitches down. Whether this is a big deal to you depends on whether climbing on a bike is a major tenet of your religion, as it is with me. The value of this can’t be understated, if you’re me. But you’re not me. This incline/decline platform sync works with Zwift, Rouvy, RGT, PerfPro, Kinomap, VirtuGo, FulGaz and others. (Please excuse my swiping the screenshot below from Wahoo’s site, but I couldn’t figure out a better way to demonstrate this.) Plus to Wahoo.

      +

      Other Stuff


      Solidity versus Ease of Relocation: The Neo Bike weighs 110lb, the KICKR Bike 93lb. The KICKR Bike, though it’s not much lighter, feels much lighter when you move it around the room, or from room to room. (Both bikes have wheels on their back legs, and roll around like wheelbarrows.) The NEO feels a little more solid, and that’s no doubt due to its extra weight. It’s about the same size as my (woodworking) router, and in the case of the NEO Bike and my router the impression you get when you try to move it is, “Geez, this thing is a lot heavier than it looks.” In the case of this kind of bike, I find that heavier is better.

      The KICKR Bike and the NEO Bike each have adjustable feet, though there’s a pedestal right under the bottom bracket that is the KICKR Bike’s first point of contact, and if you don’t adjust the feet out a bit the bike won’t sit squarely on these feet. This partly limits the capacity of those feet to stabilize the bike on an uneven surface.

      Both Bikes are easily enough transportable (though the KICKR slightly more easy); and both bikes are plenty stable (though the NEO is the one you’d say is rock solid).

      Prognosis for Additional Features: As I wrote in my review of the KICKR Bike, there are more buttons on the shifters than there are things for those buttons to do. Which is intriguing, because, what does Wahoo have in mind? Those guys’ brains are never fallow. If Garmin (which owns Tacx now) has a strength, it’s in coming out with a product that is so good that you just say, “Nailed it!” And you wonder how anyone could ever compete. Wahoo’s strength is in producing a platform – a “host” if I can use a Westworld term – and it just keeps tweaking the narrative through firmware upgrades. I wonder how Wahoo might tweak the narrative with the extra shifter buttons. So, I’ll give a soft-plus to Wahoo here, only because of a bunch of buttons that aren’t used.

      Fans: Tacx has fans. Now, you might say so what, because I already have fans. But these fans are strategically placed and because they increase or decrease their RPMs based on either how hard you work, how fast you go, you can turn them on and off, adjust their direction as you’re riding, and until you have fans like this you shouldn’t pooh-pooh their existence. That said, I alluded to a possible obstruction issue with the NEO Bike. It has been my experience that when I ride a tri bike on the trainer I have to put the TV screen well below where I’d put that TV if my road bike is on the trainer. As in, the TV is almost on the floor, angled up toward my line of sight. If this describes you, you will or won’t need to remove the fans (which are made to be removed if you don’t want them), depending on how low you need the TV; where your aerobars sit in relation to the front end of the NEO Bike; and how far your TV is in front of your bike.

      +

      Noise: Both bikes are quiet. The NEO is quieter. You… can… not… hear it if you’re in the next room. The KICKR Bike’s noise output is, to me, perfectly fine. Plus to Tacx.

      USB Chargers: This is kind of cool. The NEO Bike has a pair of these. Just like in your car. You might think, well, shoot, I have all sorts of places I can charge my devices. But if you need your iPhone’s Zwift Companion App during a Zwift ride, and your iPhone is just about out of juice, there you go.

      Price: The KICKR Bike sells for $3,500 and the NEO Bike for $3,200. They’re close enough so that I can only give a slight nod to the NEO. Soft-plus to Tacx.

      Accuracy: I have not found one whit of difference, so far, in the accuracy or precision of these units. I’m going to make a more careful study of this for future publication.

      The Upshot


      I cannot declare a winner, because the features are too much tied to what you personally need. It could come down to crank length. If you absolutely must ride with a 165mm crank, it’s the KICKR Bike. If it’s road feel, it’s the NEO Bike. Incline? KICKR Bike. Road sprints a big thing to you? The NEO Bike is a tank, and might handle those a little better depending on how much power you produce and how much you torque the bike.

      My takeaway is this. These are the best and second best (one way or the other) trainers I’ve ever ridden and the third best is a long way behind. I haven’t ridden other smart bikes. The best smart bike would be the GURU fit bike, at $18,000, only if it had a NEO or KICKR or similar resistance unit. But it doesn’t. Which brings us back to these bikes as, far and away, the best stationary experiences of my life. The only combo arguably in the conversation is a smart trainer aboard a Saris MP1 platform, and I’ll cover that elsewhere.

      +

      One last thing about ownership, and purchase pathways. I got the KICKR Bike already assembled. The NEO Bike was shipped to me. I can’t speak to how the KICKR Bike is packed. I’m sure it’s fine, but, I haven’t seen it. I have seen how the NEO Bike is packed, and I cannot imagine what a Fedex driver would have to do to damage it. (Above is the box, and when you take the side panel off the box you get this large block of styrofoam with the NEO Bike inside.)

      I own neither of these bikes, and it’s not easy for either you or I to get one. I’m reading at My Bike Shop that it looks like April for a NEO Bike, so, next week or next month depending on when in April, and next week on a KICKR Bike. Clever Training same thing as of today’s writing. All the retailers I'm looking at appear to have both these bikes available in April. Read more about the Wahoo KICKR Bike here, and the Tacx NEO Bike here.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Smart_Trainers_2020_Direct-Drive_Trainers_7427.html b/site/slowtwitch.com/www/Indoor_Training/Smart_Trainers_2020_Direct-Drive_Trainers_7427.html new file mode 100644 index 0000000..1b45a8b --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Smart_Trainers_2020_Direct-Drive_Trainers_7427.html @@ -0,0 +1,630 @@ + + + + +Smart Trainers 2020: Direct-Drive Trainers - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Smart Trainers 2020: Direct-Drive Trainers

      + +
      +
      +
      +

      We recently took a look at the rear-wheel smart trainers on the market, and now it’s time to look at the other half of the indoor trainer field: the direct-drive trainers. Based on our polling late last year, these are the ones Slowtwitchers are most interested in.

      Up until just a couple of years ago, there was a fairly significant gap between the rear-wheel and direct-drive models on the market from a price perspective. While rear-wheel smart trainers occupied the $500-600 range, the direct-drive trainers were in the $1200-$1400 range. The direct-drive trainers touted their quieter operation, greater accuracy, ability to simulate higher grades and greater wattages, and more responsive feel, but you certainly wouldn’t be wrong to ask if they were really twice as good as their rear-wheel counterparts. We’re just trying to get a decent workout in, right? Are they really worth twice the price?

      More recently, we’ve seen the introduction of a few products that bridge the cost gap a bit. New direct drive trainers around the $800-900 mark have been introduced, providing some solid options for those who would like the advantages of a direct-drive without the high price tag. With this trend, we now have a field of direct-drive trainers that range in price from around $750 to $1400, with an average just above $1000. This means that we have smart trainer options available from $300 to $1400, with no huge price gaps throughout that range. We also now have more options when it comes to direct-drive trainers than we do for rear-wheel products, as there are 13 trainers on this list, compared to the 10 rear-wheel trainers we covered in the first installment. 6 of these are under $900.

      In indoor cycling, these are exciting times. As we look towards 2020, let’s take a look at what the market has to offer.

      +

      Bkool Smart Air + Simulator - $999.99

      https://store.bkool.com/en/174-bkool-smart-air-simulator.html

      Bkool’s Smart Air + Simulator is one of the more unique-looking designs in this group. It’s designed to look somewhat like the profile of a rear wheel, which makes for a somewhat artistic take on the direct-drive concept. When you look at the statistics, it’s an impressive unit - up to 3000 watts of resistance, a maximum grade simulation of 25%, and accurate to +/- 2%. A neat added feature is that it has 6 degrees of lateral movement built in, allow your bike to move with you a bit more naturally.

      +

      Elite Direto X - $899.00

      https://www.elite-it.com/en/products/home-trainers/interactive-trainers/direto-x

      Elite’s Direto X is one of 3 direct-drive trainers in their lineup, and builds on its predecessor, the original Direto. Improvements include an increase in accuracy (+/- 1.5%), increase in maximum simulated slope (up to 18%), and increased maximum resistance (2100 watts at 40 km/h). Elite uses their OTS (Optical Torque Sensor) to improve the accuracy of the measurement without being affected by temperature and to provide their Pedaling Analysis functionality. Overall this makes for a very impressive feature set at the $900 level.

      +

      Elite Drivo II - $1199.00

      https://www.elite-it.com/en/products/home-trainers/interactive-trainers/drivo-ii

      The Drivo II claims an accuracy of +/- .5%, which would make it the most accurate trainer on the market. The Drivo II achieves that accuracy through an even more advanced Optical Torque Sensor (OTS) than the Direto X. Rated at 2300 watts of maximum resistance, 24% max grade, and also containing a 13.2 lb flywheel, the Drivo II is an impressive unit, on paper.

      +

      Elite Suito - $799.00

      https://www.elite-it.com/en/products/home-trainers/interactive-trainers/suito

      Elite’s new $800 Suito comes in at the more affordable end of the direct-drive spectrum and even includes a Shimano 105 11-speed cassette to get you started. Claimed accuracy is +/- 2.5%, maximum grade is 15%, and maximum resistance is 1900 watts. The Suito doesn’t have OTS like it’s more expensive siblings and has a relatively light flywheel at 7.2 lbs, but does still include built-in speed and cadence sensors. At this price point, the Suito stacks up nicely compared to its peers and is worth a look.

      +

      JetBlack WhisperDrive Smart - $899

      https://www.jetblackcycling.com/collections/smart/products/whisperdrive-smart-1

      The JetBlack WhisperDrive Smart is unique in that it doesn’t require you to plug it in. Matter of fact, it will even charge your USB devices. JetBlack doesn’t specify the accuracy, but it is capable of producing 2500 watts of resistance and emulating a 16% grade. The 17.5 lb flywheel is the second heaviest in this bunch (the H3 has a 20 lb flywheel), helping to provide a more road-like feel.

      +

      Kinetic R1 - $1050.00

      https://www.kurtkinetic.com/trainers-products/kinetic-r1

      The R1 uses Kinetic’s Rock and Roll technology to enhance their first direct-drive trainer. In addition to Bluetooth and ANT+ connectivity, the R1 also provides a USB port, allowing for a reliable hard-wired connection and helping to eliminate interference. Kinetic describes their flywheel as “massive”, although doesn’t give specifics in regards to the weight. The R1 can provide up to 2000 watts of resistance and simulate up to a 20% grade.

      +

      Saris H3 - $999.99

      https://www.cycleops.com/product/h3

      We’ve spent some time with the previous generations of the H3 - the CycleOps Hammer and H2. The Saris H3 is built on these solid products, matching the specifications of the H2, but coming in $200 cheaper. For $1000, you get 2000 watts of maximum resistance, up to a 20% grade simulation, and accuracy of +- 2%. Saris is the only company to provide a noise level rating on their direct drive trainer, coming in at 59 dB at 20 mph, in case you are looking to get a workout in at your local library.

      +

      Tacx NEO 2T - $1,399.00

      https://tacx.com/product/neo-2t-smart/

      Tacx just introduced the third generation of their top-of-the-line NEO, the NEO 2T. At $1400, this is the most expensive trainer listed here. The NEO line has always offered some unique features that help it to stand out, including simulation of road surfaces such as cobblestones or gravel, built-in pedal stroke analysis, and is the only trainer on the market that will actually simulate a descent. On the ascent, it will simulate up to a 25% grade, and can produce a maximum of 2500 watts of resistance. The NEO 2T is only matched by Elite’s Drivo II in terms of accuracy, with the 2T’s +- 1% rating. Like the Kinetic R1 and the Bkool Smart Air + Simulator, the NEO 2T offers some lateral movement, allowing for a bit more natural ride.

      +

      Tacx FLUX 2 - $899.00

      https://tacx.com/product/flux-2-smart/

      Tacx’s FLUX 2 is the next generation of their mid-level direct-drive trainer, coming in at around $900. The FLUX 2 improves on the FLUX S with improved accuracy at +/- 2.5%, 2000 watts of maximum resistance, and a maximum grade of 16%.

      +

      Tacx FLUX S - $749.00

      https://tacx.com/product/flux-s-smart/

      While the FLUX 2 has been introduced as the next generation of the FLUX S, we'll mention the FLUX S here as it is still listed on the Tacx website and can be had for under $750, making this the most affordable direct-drive trainer on the market. For $150 less than the FLUX 2, you give up a bit of accuracy, maximum grade, and maximum resistance, but it’s still a lot of trainer for the money. One caveat is that the FLUX S doesn’t include the adapter for thru-axle bikes like the FLUX 2 does. Once you’ve dropped an additional $50 on that, the price delta decreases a bit more.

      +

      Wahoo KICKR - $1199.99

      https://www.wahoofitness.com/devices/bike-trainers/wahoo-kickr-powertrainer

      Wahoo’s KICKR is now on its fourth generation, and has become one of the first smart trainers to come to mind when conversing with other riders. The continued refinement over the years has made for a very good set of specifications, and we’ve spent a bit of time with this ourselves. With +/- 2% accuracy, up to a 20% maximum grade, and as much as 2200 watts of resistance, the KICKR might not lead the pack in any single metric, but it is near the top across the board. Wahoo’s ecosystem allows the KICKR to interact with some of their other accessories, such as the CLIMB wheel riser and HEADWIND fan, providing some opportunities to create the ultimate pain cave.

      +

      Wahoo KICKR CORE - $899.99

      https://www.wahoofitness.com/devices/bike-trainers/kickr-core-indoor-smart-trainer

      The KICKR CORE is a fantastic response to the indoor trainer market’s gap in prices, coming in at $300 less than it’s top-end sibling. As the name implies, the CORE is a somewhat stripped-down, back to the basics unit, with a simpler, non-folding stand and slightly more pedestrian specifications. Nonetheless, the numbers are still solid, with accuracy matching the KICKR at +/- 2%, up to 1800 watts of resistance, and a 16% maximum grade simulation. Like the KICKR, it still provides compatibility with their HEADWIND and CLIMB accessories, making for a compelling option for someone interested in training in the Wahoo ecosystem.

      +

      Worth a Mention

      Not discussed above is another trainer reported to be coming soon the - Xpedo APX Pro. While better known for being a pedal manufacturer, Xpedo has their direct-drive trainer in the works. It’s not even on their website yet, so we’ll have to wait to hear more.

      +

      Stacking Them Up


      Brand

      Model

      MSRP

      Accuracy
      Max
      Grade
      Flywheel
      Weight
      Max
      Resistance
      BkoolSmart Air + Simulator$999.99+/- 2%25%3000 watts
      EliteDireto X$899.00+/- 1.5%18%9.3 lbs2100 watts
      EliteDrivo II$1200.00+/- .5%24%13.2 lbs2300 watts
      EliteSuito$800.00+/- 2.5%15%7.2 lbs1900 watts
      JetBlackWhisperDrive Smart$899.00TBD16%17.5 lbs2500 watts
      KineticR1$1050.00+/- 3%20%2000 watts
      SarisH3$999.99+/- 2%20%20.0 lbs2000 watts
      TacxFLUX 2$899.00+/- 2.5%16%16.8 lbs2000 watts
      TacxFLUX S$749.00+/- 3%10%15.4 lbs1500 watts
      TacxNEO 2T$1399.00+/- 1%25%2200 watts
      WahooKICKR$1199.99+/- 2%20%16.0 lbs2200 watts
      WahooKICKR CORE$899.99+/- 2%16%12.0 lbs1800 watts

      +

      Accessories

      Like the rear-wheel trainers, every one of these trainers includes both Bluetooth and ANT+ compatibility, ensuring that you can use it with whatever indoor training app you prefer. Bike compatibility is a bit more of a challenge, as different axle standards and hub standards muddy the waters a bit. Compatibility has improved, overall, with most providing support for quick-release and thru-axle systems. These all come with a Shimano/SRAM 8/9/10/11-speed-compatible hub to provide support for many of the bikes out there. For those with Campagnolo hubs or SRAM XD Drivers, you’ll need to purchase the appropriate adapter. Most of these do not include a cassette, so that’s one more piece to consider when you buy.

      Final Thoughts

      It’s hard to put into words just how much is going on in the indoor training space - we’re in a really neat place right now in this arena. From a consumer perspective, we have more and better choices, with new products coming out alongside solid revisions of existing products. The manufacturers continue to battle it out, continuing to raise the bar on quality, and the real winners of this battle are us, the consumers. The future is bright.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Smart_Trainers_2020_Rear-Wheel_Trainers_7424.html b/site/slowtwitch.com/www/Indoor_Training/Smart_Trainers_2020_Rear-Wheel_Trainers_7424.html new file mode 100644 index 0000000..2eefedc --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Smart_Trainers_2020_Rear-Wheel_Trainers_7424.html @@ -0,0 +1,630 @@ + + + + +Smart Trainers 2020: Rear-Wheel Trainers - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Smart Trainers 2020: Rear-Wheel Trainers

      + +
      +
      +
      +

      For those of us in the Northern Hemisphere, the days are getting shorter and temperatures are getting cooler, which means that some of us may retreat back into the pain cave. Some of us never left. The smart trainer manufacturers made their presence known at Eurobike 2019 a couple of weeks ago, and we are going to take inventory of the smart trainers currently on the market (or coming very soon) to help you find the best option to enhance your indoor experience.

      +

      In this first installment, we are going to take a brief look at the rear-wheel smart trainer options. Consider this an inventory rather than a review of each trainer, and you can look forward to more content on direct-drive trainers, smart indoor bikes, and more soon.

      +

      For those looking for bang for the buck, rear-wheel trainers occupy the more affordable end of the smart trainer segment, with options ranging for as little as $300 to $750, with an average around $495. As the products continue to evolve, accuracies have improved to as much as +/- 1% and all of the trainers on this list have both Bluetooth and ANT+ connectivity, allowing for compatibility with a variety of software applications such as Zwift, Rouvy, TrainerRoad, The Sufferfest, FulGaz and more.

      +

      4iiii Fliiiight - $599.99


      https://4iiii-innovations.myshopify.com/products/fliiiight-smart-trainer

      We’re going to start this off with the newest and most unique product here - the 4iiii Fliiiight. Known for their power meter solutions, 4iiii is jumping into the trainer market with a trainer quite unlike anything else out there. The Fliiiight doesn’t actually touch your tire - instead, the resistance is generated using a pair of robotic magnetic arms. It’s probably best to just see it in action, and DC Rainmaker has a video that demonstrates the Fliiight a bit better than what we have room for here. The Fliiiight isn’t expected to land until October, but with a claimed accuracy of +/- 1%, up to 2200 watts of resistance (the most of any of the trainers listed here), no tire noise or wear, and a battery that allows you to be unplugged for up to 2 hours, this might be worth waiting for.

      +

      Bkool Smart GO - $299.99


      https://store.bkool.com/en/179-bkool-smart-go-end-of-season-offer.html

      Their website is somewhat light on details, but Bkool claims the Smart GO provides 800 watts of resistance with a maximum simulated grade of 8%. The claimed noise level is 75 decibels at 30 kph (18.6 mph). It weighs in at just under 10 kg, or about 21.8 lbs. The swing arm design allows for support of a wide range of tire sizes, and optional adapters support today’s common quick-release and thru-axle bikes. Bkool doesn’t state the accuracy or flywheel weight of the Smart GO, but at just under $300, this is the lowest-priced smart trainer on the market and will meet the needs of many riders looking to get a good workout.

      +

      Bkool Smart Pro 2 - $449.99


      https://store.bkool.com/en/180-bkool-smart-pro2-end-of-season-offer.html

      Bkool’s Smart Pro 2 is similar in design to the Smart GO, but with some logical upgrades across the board. It provides up to 1200 watts of resistance with a maximum simulated grade of 8%, and does it a bit more quietly than the Smart GO with an advertised noise level of 60 decibels at 30 kph (18.6 mph).

      +

      Elite Tuo - $499.99


      https://www.elite-it.com/en/products/home-trainers/interactive-trainers/tuo

      Elite has introduced the Tuo as their replacement for the Novo Smart, which is why you won’t see that unit listed here. The Tuo provides up to 1250 watts of resistance and a maximum simulated grade of 10%. The distinct design uses metal and beechwood will allow you to ride in style, whether you’re working out in your pain cave, living room, or at an IKEA.

      +

      Kinetic Road Machine | Control - $569.00


      https://www.kurtkinetic.com/trainers-products/road-machine-control

      Kinetic’s Road Machine | Control is based on the venerable Road Machine frame, with the Control resistance unit being their second generation of smart resistance technology. The previous generation Smart Control resistance unit lacked ANT+ connectivity, opting instead for only Bluetooth and a USB port. The new Control unit brings ANT+ back into the picture, improving application compatibility. The Control resistance unit has a 12 pound flywheel for greater inertia to enhance the road-like feel of the electronically-controlled resistance. The Control resistance unit is rated at up to 1800 watts, which is the highest resistance of any of the rear-wheel trainers listed here. It offers a maximum grade of 10% and an accuracy of +/- 3%.

      +

      Kinetic Rock and Roll | Control - $749.00


      https://www.kurtkinetic.com/trainers-products/rock-and-roll-control

      The Kinetic Rock and Roll | Control uses the same Control resistance unit as the Road Machine, but with their Rock and Roll frame that allows the bike to move with you.

      +


      Saris M2 - $499.99


      https://www.cycleops.com/product/m2

      Saris has recently restructured their organization a bit, selling off PowerTap to SRAM, and now rebranding their CycleOps products to Saris. The M2 is unchanged, aside from the brand change. The M2 can provide up to 1500 watts of resistance and a maximum simulated grade of 15%, with an accurancy of +/- 5%. The M2 also claims to be quiet, at only 69 dB at 20 mph.

      +

      Tacx Flow - $369.00


      https://tacx.com/product/flow-smart/

      The rear-wheel options from Tacx occupy the more affordable end of the spectrum, and at $369, including the wheel riser, the Flow is a nice option for the budget-minded pain cave. Accuracy is rated at +/- 5%, with a maximum resistance of 800 watts and maximum simulated grade of 6%. Like all of the other trainers on this list, it supports both Bluetooth and ANT+ communications, so you can connect with a variety of applications. The included wheel riser fits together with the trainer to act as a carrying handle, in case you’re heading to a friend’s house for a trainer party.

      +

      Tacx Vortex - $429.00


      https://tacx.com/product/vortex-smart/

      Price-wise, the Tacx Vortex comes in just a bit higher than the Flow at $429, and for the extra $60, you get 950 watts of resistance and a maximum simulated grade of 7%.

      +

      Wahoo KICKR SNAP - $499.00


      https://www.wahoofitness.com/devices/bike-trainers/kickr-snap-bike-trainer

      Last but not least is Wahoo’s KICKR SNAP. This second-generation of the SNAP was introduced in 2017 and saw a few nice updates and improvements over its predecessor, including compatibility with the KICKR CLIMB and HEADWIND. With a maximum resistance of 1500 watts, accuracy of +/- 3%, and maximum grade of 12%, the specifications are pretty solid all around.

      +

      Stacking Them Up


      BrandModelMSRPAccuracyMax GradeMax Resistance
      4iiiiFliiiight$599.99+/- 1%7%2200 watts
      BkoolSmart GO$299.998%800 watts
      BkoolSmart Pro 2$449.9920%1200 watts
      EliteTuo$499.00+/-3%10%1250 watts
      KineticRoad Machine | Control$569.00+/- 3%10%1800 watts
      KineticRock and Roll | Control$749.00+/- 3%10%1800 watts
      SarisM2$499.99+/- 5%15%1500 watts
      TacxFlow$369.00+/- 5%6%800 watts
      TacxVortex$429.00+/- 5%7%950 watts
      WahooKICKR SNAP$499.99+/- 3%12%1500 watts

      +

      Don't Forget Your Accessories


      Due to space, we didn't talk much about skewers versus thru-axles, tire size compatibility, flywheel weights, or noise levels. You'll definitely want to look at the manufacturer's specifications on those details before you lay down your hard-earned cash.

      +

      Don't forget to budget for some accessories. If you have a disc brake bike, you'll need a thru-axle adapter, which is likely going to be an additional cost and specific to the brand of trainer you choose. While you're accessorizing, don't forget a front wheel riser. Some - like the Tacx Vortex and Flow - include a riser, while other brands might offer one separately or as part of a package deal.

      Every one of these has both Bluetooth and ANT+ connectivity, allowing for greater connectivity with apps and hardware, whether you want to run Rouvy on your phone or Zwift on your laptop. Some of these do have built-in cadence and/or speed sensor, while others will rely on separate bits from Garmin, Wahoo, or others if you want that additional data.

      Wrapping it Up


      Rear-wheel trainers have come a long way in just a few short years, and the current batch of products all offer a lot of bang for the buck, especially when paired up with some of the entertaining and effective training apps on the market today. Stay tuned for the next installment, where we'll look at the direct-drive trainers out now.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Smart_Trainers_5560.html b/site/slowtwitch.com/www/Indoor_Training/Smart_Trainers_5560.html new file mode 100644 index 0000000..50b96fa --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Smart_Trainers_5560.html @@ -0,0 +1,626 @@ + + + + +Smart Trainers - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Smart Trainers

      + +
      +
      +
      +

      Unlike children where a precocious tendency might be a sign of intellect, there is no value in a sassy trainer. What makes a trainer smart is that it does what it is told.

      +

      A proper wordsmith might precisely describe as smart an electronically-braked trainer capable of allowing external software to simulate resistance.

      +

      + +

      + +

      A trainer becomes smart when a software application controls resistance. Based on data received from a software application, smart trainers can simulate riding hills and faithfully induce specified interval intensities (wattage as the operative metric).

      One popular use for smart trainers is to virtually ride a course, or race another person. Many of the software options available for smart trainers will be discussed upcoming when we focus on virtual training and racing.

      +

      + +

      +

      Another feature pretty much mandatory with all smart trainers is the ability to quantify cycling metrics such as actual power, cadence, and virtual speed, and to memorialize a session in an exportable file.

      The smart trainer has become one of the most popular training tools in triathlon. These have been around for upwards of 30 years, and for most of that time Computrainer held a hegemony over the category. But the category has exploded in recent years and it's not the trainer itself, but the marriage of its mechanical ability with compelling software platforms. This marriage is what has fueled the upsurge in sales of smart trainers.

      +

      + +

      +

      Much of the credit for the explosion in smart trainer sales belongs to Wahoo. It was the first major smart trainer to open its operating system to third-party developers, which in turn wrote apps for smart trainers. Today, nearly all new smart trainers have followed their lead.

      Just like turbo trainers, smart trainers are also available in either direct drive (Wahoo KICKR above left) or a wheel-on designs (Wahoo KICKR SNAP on the right) and each has its strengths.

      +

      + +

      +

      Smart trainers are available beginning at a price of $500 (Tacx Vortex Smart, above) ranging up to $1600 (Tacx NEO, below). The features that scale with price are maximum watts (800 all the way up to 2500) the incline (8 percent to 25 percent) the trainer can handle, and the size of the flywheel, which delivers a more road-like sensation. Direct mount trainers will be the most expensive, and will measure power with a slight bit more accuracy and generally deliver a more natural, road-like ride.

      Nearly all recent models of smart trainers transmit and receive data from devices in all formats (e.g. Bluetooth, Ant+, FE-C), support third-party software, and allow for firmware updates.

      Of note, there are at least two trainers on the market (i.e., Kinetic Smart models and the Tacx Satori Smart) that are labelled smart but they don’t meet the criteria established above. Although each model can broadcast various cycling metrics, including virtual power, they do not allow third-party applications to electronically control resistance. But they’re not simply dumb trainers either. They’re in between.

      +

      + +

      +

      As with the original iPhone and other open-source devices, what drives the interest in smart trainers is the functionality of third-party apps. Virtual training and racing animates and extends the utility of the smart trainer. What makes this year's smart trainers better than last year's? Newer models mostly focus on reducing noise and improving the accuracy and feel.

      As with power meters, what's pushing the category isn't marginal improvements in accuracy in the electronics. Rather, it's finding new ways to use the data. With power meters that means cycling analytics. Smart trainers? It's community – the ability to train and race with others, real time, or at least to share and compare efforts with a community of riders.

      We’re now at the point where third-party makers of training and racing apps will drive smart trainer buying decisions. Upcoming is our reporting on the state of virtual training and training; perhaps this will help you make your decision on what smart trainer to purchase.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Spend_Time_With_the_Fam__7568.html b/site/slowtwitch.com/www/Indoor_Training/Spend_Time_With_the_Fam__7568.html new file mode 100644 index 0000000..c283478 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Spend_Time_With_the_Fam__7568.html @@ -0,0 +1,646 @@ + + + + +Spend Time With the Fam! - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Spend Time With the Fam!

      + +
      +
      +
      +

      We’ve got a little something special this week. Yes, we’ll have our regular 4 rides, and our 4 runs, all on the Zwift schedule of Slowtwitch runs. But!... This Friday we’ll have a special 9th run, and if you can make this one it’s going to be something special.

      As you may know, two-time Olympian (Steeplechase) Anthony Famiglietti did something pretty special a month and a half ago. He ran a 3:55 mile on a treadmill. “Fam” is coming to the Endurance Exchange conference, and on Friday, 12:30pm MST, he’s going to do that big bad treadmill run again, however this time he's noodling running 2 miles. What Fam had at his last event was an in-person crowd. But he didn’t have an in-Game crowd, and I thought that would be cool. So…

      +

      We’re going to have a run go off at 12:15pm MST, it’s 4.5 miles, and it’s going to happen at May Field. This is Zwift’s new running track, and I’ve been spending a lot of time there myself lately. You’ll see a pic I took of May Field below.

      We’ll begin running, and Fam will be warming up. You, me, we’ll all run whatever pace we each want. There’s a lot of people who run on May Field, and the most I’ve counted on a straightaway at one time is 15, I’d say 30 or 40 people are running on that track most of the time. It’s a great place to run!

      +

      There’ll be the in-Game audience (us, wherever we are in the world) and If Fam runs 2 miles, at the pace I expect him to, then you’ll be able to give him a Ride-on several times during this run. I hope I see a sea of Ride-ons all over the screen! I’m pretty sure nobody has ever been pushed to a special effort with both an in-person and an in-Game audience cheering him or her on, so, let’s see what that looks like, shall we?

      +

      In other news, there’s a new Slowtwitch kit that just debuted, maybe you saw it. (Rode in it.) This is actually the new kit De Soto made for Slowtwitch; perhaps you had a chance to buy it last year; if you didn’t we’ll see if we can get that offer up and running again for this season.

      On that run-with-Famiglietti event, you can join that event here or via our Zwift Widget on our Reader Forum. Just remember, we never actually schedule stuff MST. Do the calc in your head. that's 11:15am PST, and whatever it is in other time zones.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Stationary_Cycling_Isn_t_Seasonal_7343.html b/site/slowtwitch.com/www/Indoor_Training/Stationary_Cycling_Isn_t_Seasonal_7343.html new file mode 100644 index 0000000..7d7e925 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Stationary_Cycling_Isn_t_Seasonal_7343.html @@ -0,0 +1,647 @@ + + + + +Stationary Cycling Isn’t Seasonal - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Stationary Cycling Isn’t Seasonal

      + +
      +
      +
      +

      We announced today that Wahoo Fitness just purchased The Sufferfest and of course our Reader Forum is abuzz now, wondering who’ll purchase TrainerRoad (no one that I know of, I’m just reporting!) and what does all this mean?

      I’m happy that we’re here in July and talking about this because I’ve been writing for all of Slowtwitch’s 20-year existence that stationary bike training isn’t seasonal! Or, to the degree it is, it shouldn’t be.

      I am not a coach. Let’s all stipulate to that. But I have coached full-distance athletes in the past, who’ve stood on the pro podium in Kona, and for decades I’ve never felt that my themes and theories are worthy of secrecy: First, because I’m nothing special; and Second, because knowing a winner’s training plan isn’t going to put you on that podium any more than wearing George Clooney’s tux will get you the girls.

      +

      So, let’s talk about those workouts. They were of course athlete dependent. Did the athlete respond well to shorter mileage or longer? High or low intensity? And how quickly did that athlete got him or herself into shape? Also, back in “my day”, it was typical for there to be two or at most three peaks: possibly March (Ironman New Zealand); then anywhere from May thru July, depending on what race the athlete felt was important (Nice, Zofingen, Roth); and then downtime followed by a peak at Kona. Preparations differed depending on what race and what time of year.

      If you’re trying to get a Kona male pro comfortable riding a pace that would yield a split in the 4:20s, or a woman riding in the 4:50s, I found that the most reliable methodology (all things equal) was a mix of long rides at various paces (perhaps a half-dozen total rides in the 100 to 125 mile range, taking 6 or 7 hours, in the 6 or 8 weeks leading up to the taper); along with targeted stationary workouts that were 45 to 60 minutes long. In other words, for us, stationary was not a winter thing. It was an in-season speed weapon.

      Honestly, back then, we rode our Computrainers more in the spring/summer than we did in the winter. Stationary cycling wasn’t a big thing 25 or 30 years ago, at least in San Diego, which was where we were at that time. In fact, the first stationary trainers that came along – apart from rollers – appeared during my tenure as a cyclist. Rollers were the “multiplayer game” when I was a young cyclist. There were, I suspect still are, roller races, consisting of two hands on what looked like a pool clock, each hand belonging to a set of rollers aboard which a rider rode his bike. The first wind trainers came along in the late 70s and early 80s, and then Computrainer debuted, commanding the stage like Freddie Mercury and we had something that allowed us to finally do on a bike what we could do in the swim and run: perform precise, repeatable intervals without the interference from cars, red lights, flat tires or inconsistent winds.

      +

      Accordingly, a generation or more ago, the Computrainer and its infamous ERG mode replaced, for me, motorpacing and the other speedwork-oriented techniques upon which cyclists relied. I wasn’t the only one using a Computrainer. More than half the top American pros used them and because I sponsored so many of them with bikes and wetsuits I had some insight into their training. Yes, we all are familiar with the stories of Andy Potts and Lionel Sanders and the time they’ve spent riding indoors, but these athletes simply followed Kenny Glah and a stationary tradition already established.

      Every Tuesday I lead a structured training workout, 60 minutes long, on Zwift. I write all the workouts. They’re quite variable. Courses are different, workouts are different, themes differ, but underlying these workouts is an adherence to specific elements that participants can expect. The same efforts, same intensities, same durations, are there every week, and it’s really pretty much the same as what I prescribed a quarter century ago. Just, today, it’s “multiplayer”, that is to say, it’s a virtual spin class, anywhere between 45 and 150 riders depending on the time of year and whether it’s a big week of racing (fewer people take part if they just finished or are tapering for an Ironman).

      +

      We get our biggest crowds during the Northern Hemisphere winter of course. But we shouldn’t. We should, in my view, get our biggest crowds in the summer, for the reason I just explained. Some are catching onto this. The folks behind the Nautica Malibu Triathlon are inviting their registrants to take part in our Tuesday Structured Training sessions, and in consideration we’re going to tailor a number of our workouts specifically to this group, for this race.

      Of course training is training, and whether you’re racing the Nautica Malibu Half Distance or Ironman 70.3 Worlds in Nice, when I write our workouts, I usually think in terms of improving speed and power over a 2 and-a-half to 5 hour cycling effort.

      +

      It doesn’t need to be Zwift, of course. The Sufferfest (Wahoofest, ridden by Suffooligans?!), Trainerroad, Golden Cheetah, Rouvy, any platform that allows you to build a structured workout, any of that works. For many, me included, multiplayer is a useful tool, because of the social aspect, the participant expectation, and what you might pick up in the comments during the session. For others, who don’t want the distraction, and who don’t need the glue of the group (I am motivated by group-glue) multiplayer is not where it’s at.

      I’ll write about this some more, and get into the weeds of these sessions: What is the appropriate TSS for a 60 minute session, to achieve the sort of gains I had in mind when we were Computrainer users in the 90s and now that we’re Zwift, TR, SF and Rouvy fans today. Stuff like that. I’ll also remind you all how to get yourselves going with these stationary platforms. Folks today still ask what the technical requirements are, how to get themselves set up, so we’ll talk about that as well, because, while stationary season formally starts on Slowtwitch at the end of October, if I had my way you all would be in the thick of that season right now. (For those interested, Here's that Tuesday afternoon structured training workout.)

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Tacx_NEO_Bike_Long_Term_Review_7651.html b/site/slowtwitch.com/www/Indoor_Training/Tacx_NEO_Bike_Long_Term_Review_7651.html new file mode 100644 index 0000000..3594434 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Tacx_NEO_Bike_Long_Term_Review_7651.html @@ -0,0 +1,654 @@ + + + + +Tacx NEO Bike Long Term Review - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Tacx NEO Bike Long Term Review

      + +
      +
      +
      +

      If I ever do a “long term review,” it means I really dug what I’m reviewing. Why? Because if I don’t like some janky-arse product why would I use it for a “long term”? Life’s too short. At least my life is. More precisely my life’s remainder is too short. It is therefore no mystery why my last two “long term” reviews have been of this industry’s overwhelmingly best-in-class, best-of-year products: The Wahoo KICKR Bike and this, the Tacx NEO Bike. But I’m not going to blow smoke up anybody’s skirt today. There are things I don’t like about this bike and I’ll write them all below. That established…

      Two things happened at roughly the same time and one of those things was spectacularly, once-in-a-century, bad: the COVID-19 pandemic. The other was good: the advent of the smart bike. If we have to remain indoors and at-home, the smart bike is – full stop – the best way to ride stationary. In fact if you hop on one of these it won’t be full stop for you, it’ll be full go. Ba Dum Tss!

      I base this on having ridden a lot on a pair of smart bikes: the Wahoo KICKR Bike and the Tacx NEO Bike.

      +

      I’ve already written about this bike in the shootout between these two industry leading bikes. But now it’s just the Tacx NEO Bike by itself I’m writing about, what’s good about it; what I don’t like about it; what features it has and doesn’t have.

      Can I give you the bottom line now? If what you want is a stationary experience that is unmatched during normal riding, this is it. This is the unit. No stationary product I’ve ridden matches the ride quality of this bike. It is stable; solid; the resistance delivered is accurate; it’s comfortable; it’s roadlike; it’s perfect. Or if it’s not, I don’t know what perfection is because perfection sits on some scale of which I’m unaware.

      +

      This is what’s good about the bike, and above you can see what this bike looks like set up in the messy Slowtwitch Stationary Workshop. Now let me tell you evvvverrrryyything less good about the bike that I’ve uncovered, so you can process it, know what you’re in for, and make a decision. And I’m asking people to inform us of their decision now, if you’re reading this review at the moment of its publication. What bike are you getting next? That’s our current poll. Two interesting things: 1) As of this writing, you’re getting a gravel bike next. That’s your next bike, way more than any other. Yet, about 1-in-10 if you are getting a smart bike – i.e., this bike or a KICKR Bike – before you get any sort of outdoor bike. Which is why these smart stationary bikes are so hard to get right now!

      Back to what’s not industry leading about this bike.

      The shifting on the Tacx NEO bike is not as intuitive as I’d like and, at present, not subject to a settings change. It’s certainly something one can get used to. I acclimated. But that’s the thing: It would be nice to not have to acclimate to a product.

      The shifting buttons are here, in blue. Big and little. That's upshift and downshift. Cassette shifting on the right lever, front derailleur shifting on the left lever. On one lever, the larger button is upshift and on the other paddle the larger paddle is downshift. I can almost promise you – I don't know this for a fact but I'm almost-promising you anyway, by intuition – these shift paddles will become programmable at some time in the future. As of now, I can certainly live with how they currently shift.

      +

      The crank is adjustable, but not in a wide enough range (170mm to 175mm), and it’s adjustable with a piece on the front and back of the pedal eye that are not hard-attached to the crank. If you lose one of these pieces I don’t see how the bike is rideable until you find a replacement. There are extra pieces for other crank lengths that you must keep track of, that is, there’s a hardware set for 172.5mm, and then another for 170mm and 175mm (you just reverse the pieces and they’re either one length or the other). There are many ways to make an adjustable crankset, I’ve used them all, and they’re all superior to this method. This is not a deal breaker, but it was an unforced error.

      +

      The fans need their own on-off switch. From the cockpit of the bike, while you’re riding. The only way to keep the fans from blowing is to yank the radio jacks out from the front of the bike where they plug in (image of what those radio jacks look like below, and this is from the front of the bike); or you can disable them in the Tacx Utility app. But the bike doesn’t support multiple concurrent BLE connections, and the bike is already connected to a computer via BLE, if you’re riding (say) in Zwift. So, if you’re in the middle of a Zwift or Rouvy ride, the bike can’t talk to the app at all. For any reason. Let alone disabling the fan.

      That established, at least this bike has a fan. Actually, a pair of them, which are adjustable via the Tacx Utility App. You can omit them from the build-up of this bike if you don't like them. I like them. Just, since Tacx gave them to me I'm whining a little about how I'd like them to function in the ideal.

      +

      Then there are features the bike doesn’t have. Such as incline/decline, and when I talk to Slowtwitchers this is one feature that causes many of you to say, “Game over! I’m getting a KICKR because it inclines!” I get that. I have no quarrel with your decision.

      And, it’s questionable how ubiquitous Tacx’s road feel feature, which is one of the big things Tacx does. This feature works in Zwift. It works in Tacx Films of course. Platforms beyond these? I’m not sure.

      If you aren't aware of this feature, if in Zwift you're cruising over rideable dirt, going over a wood plank bridge, rolling over a cattle guard, the NEO replicates the road feel of all of these, and you can program in the Tacx Utility App exactly how much road feel you'll get. This has been a feature of the NEO trainer and is unmatched in the industry, to my knowledge.

      +

      Above, what you’re looking at is my saddle position on this bike. The saddle pushed almost all the way back, to the “15” marker. But my saddle position is not atypically rearward. My saddle nose in this image is about 80mm behind the BB. This means if you’re riding with tri bars on this bike you do NOT need to worry about getting the saddle far enough forward! But a lot of taller guys ride with the saddle nose more than 80mm behind the BB.

      Inset in that image above is the saddle adjusted all the way back. That’s an extra 2.5cm rearward, behind the “15” laser etch mark. So, with a typical saddle in the center of its rails, you have about 110mm of saddle-nose plumb line setback. That’s okay. That’ll take care of 49 out of 50 of you. But I’d have made the bike a little differently.

      +

      Likewise, my handlebar is pushed about as far forward as I can get it (above). But my handlebars are not that far forward of the BB. My “HX” – the horizontal distance from the BB to the handlebar clamp – is about 490mm. Yes, you can put the bars a little further forward on this bike, and you can always place another handlebar in there, that has a longer “reach” from handlebar clamp to the hooks.

      But this – curiously to me – is an issue common to both the KICKR Bike and the NEO Bike: They each are very height adjustable, and the saddle can go down to a BB-to-saddle-top distance of about 635mm (i.e., if you’re 5-feet tall you should be good, and if you’re 6’6” you should be good, saddle height-wise). But both bikes lack the sort of fore/aft adjustability that equals that massive up/down adjustability. That said, the great majority of you will fit fine on this bike, and part of that is because most non-hardcore road racers are not fit very well on their road race bikes. Most need a longer (and lower) position than they currently use, therefore these smart bikes are plenty long for the (sub-optimized) positions many or most employ. If you are a hardcore racer, you'll still find the front end long enough most likely. Just, again, I'd have made both the NEO and the KICKR Bike adjust a few cms more length-adjustable in the front.

      This exhausts my list of negatives. How do they rate? Honestly? The positives of this bike are so positive as to render the negatives annoyances rather than deal breakers. As you know by now, if you read what I’ve been writing about stationary devices in 2020, the products of the year, by far, are the two smart bikes I’ve ridden. Whatever product is in third place is so far back a Varia radar can’t even find it.

      Adjustability


      The primary values to this class of bike – the smart bike – are: First, the adjustability. If this bike is used by, say, you and the spouse, and if you use the same pedal system and crank length you might, maybe, find some way to stretch out the process of changing it over, fitwise, to 3 minutes.

      Even better, you can adjust your position while you’re riding this bike. And I mean, while you’re pedaling. Do you ever wonder while riding whether you’d like to have your stem 1cm longer or shorter? Bikes like the NEO give you great instant feedback on whether you want that or not, because you can literally make that “stem” change as you’re pedaling. The NEO Bike is especially good for this because it’s got lock-down handles easy to operate while you’re aboard the bike (in the image below, 4 handles, 2 front and 2 rear). One side benefit of this feature is that I hope we can get riders to improve the road bike positions because their aboard a bike that so easily and quickly accommodates positional experiments.

      +

      Shifting


      The second big deal about smart bikes is that the shifting is virtual. You don’t have to worry about mis-shifts, and you can choose whatever shifting you want. You program the gear ranges into the bike, which means you can change the shifting when you change courses. You neither need to change gearing on the trainer – there are no gears per se – nor do you ever run out of gears. The only issue is that multiple BLE signal thing. You can’t change your “virtual cassette” during an event, if you’ve got a BLE connection to the event, because the Tacx Utility App doesn’t support multiple BLE connections at this time. And by the way, about that Tacx Utility App, below are 3 of the screenshots from my iPhone showing settings available to you.

      +

      Below is what the console display looks like with a tablet showing Tacx Films, which is the very nice, very high quality, library of videos Tacx offers. What you see is the NEO Bike’s built-in console displaying the metrics produced by or identified by the bike. Inset into this image is what my NEO Bike console displays. Just this. Just the gearing. Why? Because I’m spending pretty much all my time in Zwift, and Zwift is showing me everything I need to see other than the gearing.

      +

      When I get on this bike, I feel like I’m strapped on, locked in, solid, perfect. Perfect. The problems with this bike are mostly just what it doesn’t do (yet). Different shift patterns. Multiple BLE connections. Other than incline, and a dodgier-than-necessary method for changing crank lengths, these issues are solved with firmware fixes. For example, today you can program in the shifting you want on the NEO bike, including 12-speed, but you can’t program in a 10-tooth 1st position cog. Why 12-speed but not 10-tooth, since this is the cog you’re likely to have in that 1st position if you’re riding 12-speed? So, firmware upgrades. This will take today’s already ridiculously cool NEO Bike and make it cooler yet.

      +

      As for how you get this bike, do not worry about getting it mail order. Tacx packages this as if it was shipping 120 pounds of nitro glycerin. They could drop this from a plane onto your front yard, with no parachute, and the box would protect the cargo. It’s easy to put together, and quick. And intuitive. It’ll take you about an hour to get up and running. Above is what the box looks like, and inset into that image is what the box looks like with one side of the box peeled away. That is a massive stryrofoam block, silhoutettes of the bike’s assemblies carved into it.

      The NEO Bike by Tacx costs $3,200. Where do you get one of these? MyBikeShop has them available in June, and freight free, which is the best deal by several hundred dollars that I’ve seen. So far, what I’ve noticed is that this bike (and the KICKR Bike) are not getting more available, but less, that is, the lead times are stretching, not shrinking. If you want to read about this bike from Tacx, here’s the company website page on this bike.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Tacx_NEO_v_Wahoo_Kickr_6160.html b/site/slowtwitch.com/www/Indoor_Training/Tacx_NEO_v_Wahoo_Kickr_6160.html new file mode 100644 index 0000000..3116b50 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Tacx_NEO_v_Wahoo_Kickr_6160.html @@ -0,0 +1,614 @@ + + + + +Tacx NEO v Wahoo Kickr - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Tacx NEO v Wahoo Kickr

      + +
      +
      +
      +

      Stationary training is getting more elaborate. And more expensive. But if anything in cycling needed a kick in the pants it was the experience of riding while going nowhere.

      Direct drive (direct mount) trainers are front and center in the evolution of a better basement experience. There are several good products: The Elite Drivo, the CycleOps Hammer, and two we’ll profile here: The Tacx NEO and the Wahoo Kickr, which I will refer to as the Kickr 2.0 to differentiate the previous model.

      +

      Spec-wise these two pencil out similarly, with the NEO holding a slight edge in all or nearly all metrics. The Kickr 2.0 costs less, at about $1200 vs. NEO $1600 (without a cassette). Slowtwitch conducted extensive testing on each of these.

      Specifications

      The new Kickr looks like its predecessor. The obvious outward difference, right off, is a much-improved carrying handle. Otherwise, the most recent prior version of the Kickr was well done, I had few complaints, explaining why there are not many updates to this model.

      +

      But there are some upgrades to the Kickr 2.0, such as improved accuracy (+/- 2%), LED connection lights, a claimed 14% reduction in sound, the maximum incline increased to 20%, and improved feel or sensitivity to virtual platforms.

      Along with the above improvements, users still get nearly universal compatibility with road and time-trial bikes, an 11-speed cassette, a Wahoo RPM, along with the ability to pair the Kickr to third-party power meters (the Wahoo Kickr can sync to the bike’s on-board power meter), and a maximum resistance of at least 2000 watts.

      The NEO exceeds virtually all the specifications of the Kickr. The NEO claims a greater power accuracy of 1%, greater maximum incline of 25%, and a maximum wattage of 2200 Watts, and although not expressing the noise the trainer makes in decibels, the Neo boasts being the quietest trainer on the market.

      +

      Along with the above differences, there are few other subtle differences that can get overlooked. The NEO does not require a power source to operate unless you want trainer control features; it doesn’t require calibration; and it can simulate downhills and different types of road surfaces such as cobblestones when used with virtual platforms such as Zwift. The NEO’s execution of this is very impressive.

      Compatibility

      Both the Kickr 2.0 and the Tacx NEO are compatible with all the major platforms (e.g. Zwift, TrainerRoad, Sufferfest) and nearly every other software or mobile application on the market today. They both use Ant+ FEC and Bluetooth Smart allowing third-party apps, mobile devices, and virtual software programs to control either trainer. Of note, unlike Wahoo, Tacx has its own platform of virtual rides and training available for purchase, Windows only (a little more on this below).

      Both trainers also have connectivity lights to indicate when peripheral devices are connected via Ant+ or BTLE, a minor but nice feature. The Kickr 2.0 and NEO can also re-broadcast data in both Ant+ and BTLE including speed, cadence, and power.

      +

      While both trainers should be compatible with almost all bikes, the NEO did have some issues with certain TT frames, and there has been a running change. Prior to purchasing a NEO for a TT style bike, confirm with the retailer if “W2017” is on the serial number as found here on the Tacx support page. Also, there is a very active Facebook Group found here for Tacx Neo Owners that I recommend to any current or prospective owners.

      Sound, Accuracy, and Feel

      Direct drive smart trainers tend to be the quietest and most accurate trainers, and better replicate road feel versus their wheel-on counterparts. Sound and accuracy are easy to quantify and compare but feel is subjective.

      The Kickr 2.0 boasts an accuracy of +/- 2% while the Neo lists as +/- 1%. To put that in comparison, the P1 pedals I used to compare report a +/-1.5% error rate and my Pioneer power crank based power meter is +/-2%, so, the Kickr 2.0 and NEO are within error bands of some of the top performing power meters. My testing has them consistently within a couple of watts of P1 pedals at various intervals and in my testing I could not find that one distinguishes itself above the other in accuracy.

      Which of these trainers is quieter? I tested the Tacx NEO and Kickr 2.0 riding at approximately 200 Watts, at 20mph, and 75-80 rpm on a freshly lubed drivetrain. The ambient noise was 48 db in the room. The Tacx NEO averaged 57db while the Kickr 2.0 averaged 61db. The difference seems more significant due to the different frequency of sound made by the Kickr 2.0 vs the NEO. There is also less floor vibration with the NEO compared the Kickr 2.0. These features may explain why the NEO seems to be quieter than the Kickr 2.0. Testing on different flooring might yield a different result.

      +

      In comparing the Kickr 2.0 and the NEO, there is a palpable difference in road feel. With the NEO the overall feel while pedaling is smoother. Ascending and descending feels a bit more natural with the NEO, as well as the feel-the-road features offered by virtual platforms, Zwift inclusive.

      Practical Considerations

      Sometimes, buyers overvalue minute differences in the specs in competing devices and undervalue what is realistic or practical. As a tech editor I’ve been guilty of this. Will you or I notice the difference between 1.5% or 2.0% error rate? Is the fact that a trainer peaks at 2000 watts instead of 2200 relevant? Can users perceive a 4db difference between a pair of trainers?



      Some considerations are more practical. I learned that my basement floor, where I station my indoor trainer, is not level. Were it not for my ineptitude as a carpenter I likely would have failed to appreciate the importance of the adjustable legs on the Kicrk2, a feature not on the Tacx NEO.

      Likewise, the NEO is a device you don’t want to drag around. It is heavy, bulky, and there is no easy way to carry it. It folds into a nice low-profile design (a pic of both trainers folded up is above), but it is a stay-put trainer. Because Wahoo made that big improvement to its handle it is the only smart trainer I bring to someone else’s PainCave for a workout.

      +

      On the flip side, the Tacx NEO is without question the most stable trainer I have tested. Also, it feels the softest on a carbon frame, somewhat due to its wide foot platform but more importantly that softness is a result of four hinges on that Neo providing just enough give during hard efforts.

      Lastly, as more devices move towards ecosystems, or integration, buyers need to consider how their current devices might interact with their new devices, such as a smart trainer. The BSX Insight will not control the Tacx NEO during a BSX lactate test, but it does function with the Kickr 2.0. The Wahoo Elemnt was designed to work with the Kickr and Kickr 2.0, and does so in amazing fashion. Owning a Wahoo Elemnt might be a deciding factor between Kickr 2.0 and Tacx NEO.

      Conclusion

      Direct drive smart trainers are more refined than they were, slightly more accurate, slightly (or more than slightly) quieter, and somewhat less expensive. Certainly, the Kickr 2.0 and Tacx NEO both meet the needs of any cyclist and are among best-in-category. The Kickr 2.0 is a clear value winner, and also gets the nod for users of BSX Insight and Wahoo Elemnt. If I regularly transported my direct drive trainer the Kickr would be it. I can suffer transporting the NEO occasionally, but not regularly.

      While the NEO costs an extra $400 (plus the cost of a cassette) it’s more stable, it offers a better feel and features for virtual rides, and it is marginally quieter. The NEO is a better choice for larger or more powerful riders.

      Beyond the scope of this overview is the software trainer companies provide, but, the suite of options Tacx provides are probably underappreciated and certainly underreported (at least here on Slowtwitch). Its Films (above is a 1-minute promo video) are terrific fun and take full advantage of the NEO’s road-feel.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/The_Birth_of_Zwift_7662.html b/site/slowtwitch.com/www/Indoor_Training/The_Birth_of_Zwift_7662.html new file mode 100644 index 0000000..7951956 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/The_Birth_of_Zwift_7662.html @@ -0,0 +1,648 @@ + + + + +The Birth of Zwift - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      The Birth of Zwift

      + +
      +
      +
      +

      How and when did Zwift start? Zwift’s co-founder and current president, Eric Min, saw an online post by programmer Jon Mayfield who, according to Wikipedia, described a "3D trainer program" he was developing as a hobby project. You can still read Jon’s post, on the Slowtwitch Reader Forum, dated April 14th, 2012, and the user reaction to it: 86 replies, more than 26,000 views.

      Below are Jon Mayfield’s words from that post, and a second post in the thread where he answers questions from a few users. The strikingly well-formed images are from Jon’s post back then. What you see in that post was the beginning of an obsessive experience that has enhanced the riding experiences of so many cyclists and triathletes.

      +

      “Being in SoCal I don't get to use my trainer that much, but when I do it sucks. Over the last 2 winters I spent a little time writing a 3d program to make training less dull, and I've logged quite a few hours on it over the last 2 winters. I figured it'd be worth showing, since it's kinda neat and I think this crowd would get a kick out of it.

      “First, the overview: Its fully ANT+ Compatible (reads heartrate, cadence, powertap), it saves out standard Garmin .FIT files for WKO+/TrainingPeaks/Whatever, if you don't have a powermeter it'll save out a fit file with power by estimating your watts using standard trainer speed->power equations, it runs on MAC or PC, and PC has full 5.1 surround sound support. There are AI racers of various speeds you can try to pass, there are special interval modes (you program in a whole workout, or just ride around the scenery, or both) with voice prompts telling you when to start/stop the interval, and I built a from-scratch PC/MAC joystick for the front wheel to sit on so you can steer around the world. It also has a video playback mode where it plays most non-DRM'd videos but still has the interval prompts and graph overlays, but that is a bit flaky at the moment and its PC only (no MAC video yet).

      +

      “When I wrote the first version back in 2010 I had to "crack" the ANT+ packets, which took me a couple weeks....weeks after I finished figuring out what the packets meant, the ANT+ group released full specs to everybody. At that point, I figured there would be all sorts of commercial apps available so I didn't work on this thing almost at all during 2011. I just used it for my workouts, and that was that. Now that it’s April 2012, I'm still pretty shocked there aren't half a dozen things like this commercially available.

      “ANT+ data comes through a normal ANT+ usb stick. I haven't messed with the ANT+ stuff in over a year so I couldn't tell you right now the update rates, but I do know that HR packets seemed pretty random, cadence/speed was about once a second (and at slower pedaling speeds repeat packets would be sent with no differences between the previous packet), and PowerTap I simply don't remember. The app has a full built-in command console and ANT+ logs can be enabled, and the whole thing dumps raw data out to a txt file so I could look sometime later if people were really curious. I do smooth the speed data a little, but I'm not totally happy with that. I need to add in the 3s/10s/30s power displays, right now watts is instantaneous. Every test run I've done, I also recorded on a Garmin Edge 500, and once uploaded to Garmin connect both my and Garmin’s fit file pretty much match exactly so I think the data I'm grabbing is "good enough" right now.

      +

      “As for graphics chugging, the whole graphics system is configurable. It'll run at 640x480 with no realtime shadowmapping, no antialiasing, etc at 400fps on my machine, or, as in my screenshots, you can crank it up to 1080p with 4x AA and it goes 40fps. All depends on what your PC can do and how much you care about graphics. On PC and MAC, I use OpenGL for graphics. On PC I use XAudio for 5.1 surround sound generation, and on MAC I use OpenAL (which *should* give me 5.1, but I can only get 2ch audio out of it right now. *shrug*).

      “There is no more ‘controller’ or custom circuitry for anything ANT+. The joystick I built uses a USB chip that makes it super easy to create a fully MAC/PC compatible joystick, so I went with that. I think it updates at 30hz or so. I doubt I'll want to get into the custom joystick manufacturing biz, and steering is 100% non-essential (it's neat for 'game' type stuff, but for workouts its just not needed) so I may just keep that stuff to myself.

      +

      “There is one last party trick this thing does, which will never see the light of day...when I do plug my old obsoleted Arduino microcontroller board in, it has an X-10 chip in it and will control an X-10 dimmer switch anywhere in your house. Plug a fan into that X-10 dimmer, and voila....the fan speeds up and slows down with your speed on the trainer. Full blast is hit around 18mph. Again, this will fry half the fans on the market (the motors don't like the type of dimming the X-10 switch does...it’s really meant for incandescent bulbs), so it's a "me only" kinda thing. Still fun to see it work.

      +

      “I should probably turn this thing into a product, but for now, it's just used by me.

      “Lemme know what you guys think, and suggestions for things you'd like to see would be interesting to hear too.”

      Yes, Jon, I think you should probably turn this thing into a product!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/The_Jewel_in_Racermate_s_Crown_Up_for_Sale_6716.html b/site/slowtwitch.com/www/Indoor_Training/The_Jewel_in_Racermate_s_Crown_Up_for_Sale_6716.html new file mode 100644 index 0000000..0344155 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/The_Jewel_in_Racermate_s_Crown_Up_for_Sale_6716.html @@ -0,0 +1,610 @@ + + + + +The Jewel in Racermate's Crown Up for Sale - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      The Jewel in Racermate's Crown Up for Sale

      + +
      +
      +
      +

      Before Wahoo, before direct drive trainers, smart trainers, or Zwift, or TrainerRoad or Sufferfest, there was Computrainer.

      Stretching all the way back to the 1980s, Computrainer was the stationary trainer that was a generation ahead of everything else. But the next generation caught up, and Computrainer found itself pressured by wireless, better graphics, direct drive, real time community.

      Computrainer still remained a favorite of spin and ride studios because you just... couldn't... break the darned thing. But for home use Tacx, Wahoo, CycleOps and others swarmed Computrainer, and Wahoo's Kickr has become a studio favorite.

      +

      So, Racermate reluctantly pulled the plug, said its goodbyes, and liquidated.

      And then I got a phone call from its founder and owner, Chuck Wurster, which was a pleasure because I have a lot of affection for Chuck - as do you if you've been to the Chicago Triathlon, Interbike, Ironman, or any of the places we've seen Chuck over the past 30 years. Turns out Velotron was still going. But alas, it's on the sales block as well.

      +

      Velotron is for some the boutique, luxury indoor trainer because, while it costs, there is very little to match the feel and arguably the unparalleled accuracy of it. But the major market is educational institutions, and more Velotrons are sold than you might think (I was certainly surprised!).

      Velotron, the brand and the company, must be sold and delivered to its new owner by July, because that's when the lease is up. If interested parties contact me (you can find me on our Reader Forum), I'll relay your interest to Chuck.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/The_Kickr_Bike_Long_Term_Review_7608.html b/site/slowtwitch.com/www/Indoor_Training/The_Kickr_Bike_Long_Term_Review_7608.html new file mode 100644 index 0000000..5e520cb --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/The_Kickr_Bike_Long_Term_Review_7608.html @@ -0,0 +1,658 @@ + + + + +The Kickr Bike: Long Term Review - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      The Kickr Bike: Long Term Review

      + +
      +
      +
      +

      As was the case after September 11th, it’s hard to write anything here without acknowledging the backdrop of world events. But here we are. And life goes on. Life is going on indoors, for many or most of us, so I thought it not wholly inappropriate to write about this product.

      It’s Wahoo’s Kickr Bike. Yes, I’ve already written about this. Last September I wrote about its existence. Now I’m writing about it having had it in my workshop for a few weeks. I know a lot more about it now than I did then, and since I’m writing about it today my time to give it back is approaching, and this won’t be easy.

      +

      The bike came to me assembled, so I can’t speak to how it’s packed and shipped if one were to buy it online (assuming that is or becomes possible). The electronics were separate, and what I can say is that it’s easy and intuitive. You’ll notice 3 radio ports for the shifters, and 2 jacks. It doesn’t matter where you plug them in, they’ll function. Before we hike our legs over the top tube let’s talk about set up.

      You configure this bike using a Wahoo app, and you’ll see on my iPhone screen that I have a pair of Wahoo apps. One is for the ELEMNT family of head units, and it’s not that one. It’s the black Wahoo icon. This is what configures Wahoo’s smart trainers.

      +

      The Kickr comes with a 110v (in the U.S.) power cord and adapter. Plug it in. Boot up this app and you’re given the screen below on the left. If you don’t see an actual Kickr unit like in the image of my iPhone below, turn the cranks a little, just with your hand, to wake up the bike.

      When you click on the blue rectangle denoting the bike, you get the center screen. Here you can rename the bike and then let’s just say you’re going to connect to a third party app, such as Zwift (and what I describe below is also the case with Rouvy, Virtugo, Tacx videos, et al, and there's a compatibility chart for this).

      Bypass the rest of this screen (just scroll down) and below Workout Profiles you’ll reach the screen I show on the far right below, whichs is Bike Profiles. Click Configure.

      +

      At this point you’re given a granular set of shifting options. Both screenshots below are from the same page in the app; you just scroll down to see all the options. On the left is the style of braking and shifting. Let’s leave this for a moment.

      As you scroll down you’ll find your front options, notably 1x versus 2x. if you choose 2x you choose your rings and your number of cogs. Then you choose the particular cassette. Don’t get overly enamored with your own pedaling prowess. When you’ve already ascended 3000 vertical feet of Alp d’Zwift, you have 500 more feet to go, and you’re riding up 13 and 14 percent grades, you’ll wish you’d chosen a different gear range if you select poorly now.

      +

      Then we get to fit, and Wahoo is trying to play well with others here. As you see, once you’ve gotten through the part described above you’ll be told how to translate your fit coordinates to the Kickr Bike. You’ll see a screen that gives you Most, Somewhat, and Least accurate options and, honestly, I’d bypass all of that.

      If you want to take a crack at translating from your Retül report to the fit bike, using the Wahoo tool, knock yourself out.

      +

      However, in my opinion, you’re best off simply measuring on your existing bike saddle height, saddle setback, and then the height and forward protrusion of whatever your contact points are. Contact points in this case will be your armrests (if you’re putting some aerobars on this bike) or Web X, or hood trough, or whatever it is you call the point on the hoods where horizontal turns vertical. Measure the “stack” and “reach” of those points on your bike and keep at it on the Kickr Bike until you nail it.

      This bike is very adjustable. The only tricky thing about it is the seat tube or the proxy for it. It telescopes, there’s a handle on the backside of it, you turn the handle from vertical to horizontal, there’s a push knob, and that knob allows you to raise or lower the seat tube. If you find your can’t get your handlebars high enough, you need to raise the seat tube. If you can’t get the bars low enough, lower the seat tube. Then, for micro adjustments, there’s a handlebar “quill” and a seat post that adjust with quick releases.

      +

      Another set of quick releases let you move the handlebars further out by lengthening the “top tube” from the midpoint forward, and then you can move the saddle fore and after by lengthening the top tube from the midpoint rearward. There’s a pair of handles (in the image above), and when you turn those handles perpendicular to the top tube you can then slide the handlebars and the seat mast in and out. Then you lock the handles back down.

      I have not calculated how short or tall you might be before this bike would not fit you. Wahoo says it'll work for riders from 5'0" to 6'4", but I think you might be able to make it work if you're a bit over 6'4", depending on your specific fit coordinates. I don’t get the sense Wahoo sabotaged themselves by disqualifying a large swathe of the audience.

      +

      Let’s revisit the “style of shifting” I mentioned above. If you look at the shifter paddles, there is a way to shift like you would a Shimano lever, with 2 discrete paddles, 1 for upshift, 1 for downshift. SRAM uses 1 paddle only. You can configure these to work either way. Likewise front derailleur performance.

      I have mine configured SRAM, so, pushing either of the 2 left hand shifter paddles places me in an easier gear, and in the harder gear when I click a right hand paddle. When I click left and right at the same time, I shift the front derailleur.

      +

      You can choose the style of shifting you want regardless of the gear options. For example, what I have in my default configuration is very much a SRAM AXS set up: 12sp, 10-33 cassette, but I could rig this to shift as if I had Shimano shifter action on it.

      The crank length is adjustable, as you see in the image above. One thing that’s quite nice about bikes like this one is that you get to simulate what your next bike, or bike fit, might feel and ride like. In fact, let’s talk about how this bike rides.

      +

      If you look at the shift levers, there’s a pair of buttons on the top. These are for incline/decline. There are similar buttons on the other lever but they are not operative. There are some other buttons on the levers not operative. I assume they’re there in case Wahoo thinks up something else, and turns them on via a firmware update.

      That said, if you’re letting a 3rd party app like Zwift control the bike, you won’t use those buttons. Let’s talk about this.

      +

      This panel, to the right hand side of the top tube, just behind the head tube, is the brains of the operation. You’ll notice a blue light, and that light is either illuminated next to a lock or an unlock icon. There’s a grey button on the right of this box, and pushing that button toggles between them.

      Lock is the setting you want if you want to control the incline yourself. Unlock is the choice when you want a 3rd party app to control the Kickr Bike for you.

      +

      When I ride this bike I’m Zwifting almost 100 percent of the time. Therefore, I want the unlock icon showing. In this case, as the road pitches up or down the bike pitches up or down. The normal display is gearing, and what you see above is the front ring (1 or 2, and 1 is the small ring), and then you see the rear cog (1 thru 12, and 1 is the inside cog). In this schema, 1 is always the low gear, easiest to pedal. 1 and 1 means that’s it, hope you chose a low enough gear for what Zwift is giving you. if you see 2 and 12 (on a 12sp set up) then this is what you’ll use for pedaling down a descent.

      If you push those incline buttons on the top of the lever – which you’ll only do if you want to override what Zwift is serving you – then you’ll briefly see the percent grade, up or down, that you’ve chosen (image below), and then the display will automatically revert back to gearing.

      +

      The bike serves you a satisfying little clunk when you shift gears. Enough to tell you you’ve shifted, but not distracting. Not having to worry about mis-shifts, choosing the gearing you need, not needing to worry about whether your wheel axles (thru or QR) matches what’s on the smart trainer, how to put an 12sp cassette on a trainer that doesn’t have an XDR cassette driver, is really nice.

      But let’s talk about one thing in Zwift that could confound you, and I’ve heard about this but I haven’t experienced it. You have a number of places you can configure stuff in Zwift. There’s your account, where you type in your gender, height, weight and (most importantly) your payment info. And then there’s in The Game, and when you’re in The Game, there’s yet again a couple of options.

      +

      You can go in and change your height and weight if you click where you see the red arrow pointing. But the green arrow gives you some options in how The Game itself responds. Sounds and appearance, stuff like that. One option is how much real, live, actual, "trainer difficulty" you want The Game to serve your trainer. In other words, if you’re 170 pounds, and you’re riding up a 16 percent grade, and you’re in a 38x25 gear, do you want The Game to serve that difficulty to your trainer? Fully? You can calibrate that. You can lessen that if you want, via that slider below.

      +

      If you do, then The Game will cut you a little slack on the steep grades. It won’t let you cheat. You still have to pedal the power to get the performance. I bring this up because I’ve heard that if you don’t have that slider pushed to 100 percent realism, that might affect whether the Zwift will drive the incline in the bike. I haven’t found that this slider affects this. I find that the Kickr Bike I’m riding faithfully obeys the grade that Zwift serves it.

      What do I not like about the Kickr Bike? There are some things. First, I’m very much into having a home stationary bike on which I can experiment. I'm spoiled. I’ve used my fit bikes, built for bike fitting, as stationary bikes for years. I can change whatever I want and see how that works for me. That’s nice. I can do that with the Kickr Bike. However, this bike has a standard seat post, with a pair of Allen screws that adjust the tilt. You’ve used these. Loosen the rear facing Allen screw, tighten the forward one, the saddle is tilted down in front. Purely Custom’s fit bike uses the same design, except instead of Allen screws there are large thumb screws you can hand tighten. These might be less useful on the road, but they’re very used on a stationary bike. The Kickr should move to this.

      +

      Second, this bike has the same lack of road feel when you’re out of the saddle as when you’re on any stationary bike. This is especially felt when you’re out of the saddle on an ascent. This isn’t a slam on this bike in particular. It’s just that Wahoo gives you something really cool: a bike that ascends as Zwift’s route ascends. What that unmasks is the unfortunately lack of road feel when you’re standing on the bike while you’re on that ascent. This is an argument in favor of, say, the Saris MP1 platform. But, the platform doesn’t allow the bike to pitch up, only to ride level. And, while I have a trainer on an MP1 platform right next to the Kickr Bike in my studio no, I can’t put the one on the other, per both Saris and Wahoo.

      One thing, as a point of order, you’ll see a device in my pictures of the Kickr Bike (such as the image just above), it’s what the saddle is attached to, it’s called a SwitchIt, and it allows me to change saddles in 10 seconds. It’s not part of the Kickr Bike.

      As for specs, 2200 watts, 20 percent incline, 15 percent decline (I haven’t ridden down that kind of decline yet; that might scare the poop out of me were this bike to point nose down like that); it’s 93 pounds total, and it rolls around on back wheels like a wheelbarrow.

      This is a great product. Well thought out. Well executed. It sells for $3,500, a very fair price for what you get. If I’d have been asked, not knowing the price, and knowing what goes into bikes like this, I’d have guessed more like $4,500. If you want the functionality of this bike – the smart trainer, and the incline via a Kickr Climb – you can get that for less than half the $3,500 price. I see that bundle for around $1,600. But, you don’t get the virtual gears, which I see as a key feature; you don’t get the adjustability.

      While the price for the Kickr Bike is more than fair, it's not pocket change, so from time to time I remind you of The Pro's Closet, or however it is you want to divest yourself of your "training furniture." If you were to buy a Kickr Bike at your local bike shop, first let TPC know what you've got gathering dust. They'll give you vouchers. Haul your stuff down there, your LBS will take it from you, you don't have to pack or ship it. Give your TPC vouchers to your LBS. This makes the Kickr Bike $3,500 minus your voucher total. I mention this because of the ever-growing gap between what we can afford, and what we are charged.

      The Kickr Bike has been conspicuously unavailable since its intro last autumn, but is now finally available in the U.S. It will arrive soon in the UK and Germany. Here’s where you can see and read more about the Kickr Bike.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/The_RUNN_Wireless_Treadmill-to-Zwift_Sensor_7614.html b/site/slowtwitch.com/www/Indoor_Training/The_RUNN_Wireless_Treadmill-to-Zwift_Sensor_7614.html new file mode 100644 index 0000000..dfaa98e --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/The_RUNN_Wireless_Treadmill-to-Zwift_Sensor_7614.html @@ -0,0 +1,652 @@ + + + + +The RUNN Wireless Treadmill-to-Zwift Sensor - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      The RUNN Wireless Treadmill-to-Zwift Sensor

      + +
      +
      +
      +

      Do you want to run on Zwift? If so, what your real legs are doing must be mimicked by your avatar’s legs on Zwift. How do you make that happen?

      One easy way is for your treadmill to communicate with Zwift. Wirelessly. It would do that by emitting a Bluetooth signal. Does your treadmill emit that signal? Just because your mill has a Bluetooth icon on it, that doesn’t mean it sends a signal. What that icon might mean is that it receives a Bluetooth transmission, from a heart monitor. That said, my treadmill does send a BLE signal and yours might. If it does, you don’t need anything else.

      +

      There are devices you can stick into your treadmill. One is called GEM Retro and it’s made and sold by North Pole Engineering, the same company that sells the RUNN that I’m going to review now. Both devices sell for $99. I mention this because if your treadmill does not send a BLE signal already, and you’re going to rig up something to make it send that signal, there’s a case that can be made for buying a GEM Retro instead of a RUNN and I’ll make that case further below.

      Just, the GEM Retro only works if your treadmill has a CSAFE port. Before you make a buying decision, make sure your treadmill: 1) Has a CSAFE port, and 2) Isn’t already BLE enabled. Here’s one way to know if your treadmill already sends out a BLE signal: Boot up Zwift and see if it detects a signal. And by the way, the GEM Retro is what was the Wahoo Fitness GymConnect. Wahoo sold this product line to North Pole Engineering a couple of years ago.

      Now then, let’s say your treadmill is deaf and dumb, it sends no signal, and has no CSAFE port. The easiest, cheapest way to get your virtual legs to mimic your actual legs is via a footpod. $30 or $40 and you’re gold. Or, spend a little more and the Stryd footpod gives you some extra functionality. Why would you spend more than double the money on a device that does basically what a footpod does?

      +

      Two reasons. First, precision. The RUNN is, as well as I can tell, the most precise determinant of the speed and distance you’re actually accomplishing aboard your treadmill. How is it more precise than the treadmill itself, assuming the treadmill has built in BLE? Because treadmills are notoriously imprecise, and because you can’t calibrate a treadmill. (At least I can’t calibrate mine; though I could imagine a pretty easy way for treadmill companies to integrate that functionality.) The RUNN has a pair of optical sensors that measure the time it takes for a light-sensitive sticker, mounted on the treadmill belt, to pass from one sensor to the other.

      Above is what you get when you open the box with the contents of your RUNN. In my opinion, the RUNN’s included Quick Start Guide is incomplete and could be written better. I recommend you refer to the online guide, with its textual step-by-step instructions and included video. Installation of the RUNN on your treadmill is quick and easy.

      Mostly, you need to make sure that the included stickers, when placed on your treadmill’s belt, pass under the optical sensors, and if stickers on the belt pass under that row of circles on the top of the RUNN unit (below), as it hangs over the belt.

      +

      I have a Woodway 4Front, and the RUNN isn’t compatible, out of the box, with the 4Front, in my opinion. It’s fine. It’s usable. But it’s not ideal. Why? Because the belt on a 4Front doesn’t sit below the treadmill's frame. There’s a mandated gap between the sticker and the sensors, which is between ¼” and ½”. There’s a cradle into which the RUNN unit sits, and that cradle has a graduated set of plastic tabs that grab the RUNN unit, and you place the unit higher or lower in the cradle depending on your need.

      However, I found that cradle flimsy. If you press down on the RUNN unit with any force at all, that force overcomes the ability of the cradle to hold the RUNN unit. Why would I ever need to touch the RUNN unit? As we’ll see, there’s a button to push to engage the unit, and there’s a battery to charge with a micro USB cable. It wouldn’t be a problem if the unit were sitting flush with the bottom of the cradle, or maybe a short distance up, but with the 4Front I had to have this unit at the very top of the cradle.

      +

      I believe the higher in the cradle the unit sits, the less capacity the cradle has to support the unit, and any time you molest the unit, by plugging in a charging cable for example, the unit comes free from the cradle. In the image above you'll see that the unit is sitting as high up in the cradle as the cradle allows, and sensors are still probably not high enough above the 4Front's belt.

      No problem. It just calls for a little MacGuyvering. I needed a *thing* that would sit in between the unit and the cradle, basically a solid foundation for the unit. So I rooted around the workshop until I found something, and with some 2-sided tape I affixed everything to everything else. I still didn’t have quite enough of a gap between the belt and the sensors, so I fashioned about another 1/8” worth of rider underneath the cradle and, presto, all good.

      +

      The way you activate the unit is to press the button on the back of the unit twice. And then once. This was (and remains) a little confusing to me because, well, here’s what the directions say: “Press the button on the back of the Runn™ two times followed by a single press to turn on the sensor strip indicator.” I don’t know what the difference is between that, and pressing the button 3 times. What is the gap between the first 2 depressions of the button and the… 3rd? Furthermore, the unit actually began to read the sticker after the first 2 button pushes. RUNN's folks say the taps on the button "are meant to be brief but enough to engage the switch. The double tap sequence is used to trigger the special configuration mode of the Runn whereas a single tap is used to toggle on/off the ANT radio." As nobody seems to complain about this but me, I'll assume this is not an issue and is mostly a manifestation of my foggy brain.

      How does one know the unit is reading? Because there are blue LEDs on the top, and they alternate as the stickers pass underneath. You can tell when they’re reading just by looking at those LEDs. When those blue lights turn on, or off, when a sticker passes underneath, the unit is reading. At a certain point those blue lights stop that behavior and they simply remain lit. That doesn’t mean the unit is not reading. There is a 60sec timeout for the "strip indication." If there's any question in my mind about whether the unit is sending, I just increase or decrease the speed on my treadmill and, behold, those changes in my treadmill's velocity are noted by Zwift. When I return to my treadmill a day later and commence another Zwift run, I find I don't need to press any button or do anything. The unit reads automatically.

      +

      When you kick start Zwift, you’ll be asked to choose a device. Always. Ride or run. Zwift will detect a device automatically. Let’s say the device Zwift detects upon boot is your footpod, and you want to change it out for the new RUNN unit you just installed. You just click on the device, on the screen, and up comes another screen with all the connected devices available to you.

      +

      You’ll now choose the device you want. My treadmill has a factory-installed, built-in, BLE transmitter. I have also installed a GEM Retro in the mill’s CSAFE port. Then there’s the RUNN just installed. You can see them all transmitting to Zwift.

      You’ll note that there are 3 connectivity types Zwift offers: Run Speed, Cadence, and Heart Rate. You should click the Cadence box too, and you’ll see that the RUNN is an option there. the RUNN optical sensors note belt vibration, and translate that to cadence. I have not found, myself, in my limited use, that the cadence metric is reliable and stable. But I’m early in my use of it. The RUNN unit does also log elevation gain and Zwift does keep a record of the elevation you've gained for every run.

      The RUNN device has its own rechargeable battery. It’s recharged with an included micro USB cord. That cord is too short for my purposes because, as you see, I’ve pretty much hard-attached the RUNN to my treadmill via the shims and 2-sided tape. But I have a USB extension cable and I now just have this thing plugged in 24/7. Here below is the the final installation of my RUNN unit, with MacGuyvered shims.

      +

      As to the GEM Retro, why and when would this be a better option than the RUNN? First, there’s no charging. You don’t have to worry about stepping on the unit inadvertently. The GEM Retro is hidden inside my treadmill, plugged in. And it has one more function laying in abeyance awaiting the day. The GEM Retro is not a transmitter. It’s a transceiver, that is, the communication is 2-way. If Zwift ever chooses to let the Game control the treadmill, the way the Game controls your smart trainer, the GEM Retro can receive and deploy those commands. Specifically, I’m talking about incline. My hope is that someday Zwift might pitch my treadmill the way Zwift pitches a Wahoo Kickr Bike. What is needed is an algorithm that automatically decelerates the treadmill as the pitch increases. But this is a discussion for another time. The point is, the GEM Retro does have the capacity to manage that.

      There is another downside to the RUNN. There’s about a 2 or 2.5 second delay between a change in treadmill speed and the time Zwift displays that behavior. In that way it’s a lot like a smart bike trainer. Have you ever pedaled like crazy to catch a group in Zwift, and you stop pedaling right as you reattach, only to find you’ve ridden right thru the group? And then the group passes you back and drops you again because you’re soft pedaling? That exact lag is what you get with the RUNN. Why? I think part of that is the sampling interval. If you have, say, only 1 sticker on your belt, whatever change you make in the treadmill isn’t known by the RUNN unit until that sticker goes by the sensors. Secondly, it takes the treadmill some time to actually respond, mechanically. When I push the button to increase the treadmill's speed, that increase request is known by the mill immediately, and told by the mill to Zwift immediately, and Zwift displays that immediately. But the RUNN doesn't know that you've pushed a button. It only knows how fast a sticker on a belt passes by a pair of optical sensors. It takes a moment for the motor to get the mill up to speed and that lag, plus time it takes for a sticker to make its way around to the sensors, adds up to about a 2.5sec delay. For this reason, I would recommend placing perhaps 3 stickers equidistant around the belt, to help sync your treadmill commands to the Zwift display as quickly as possible.

      Why not choose the GEM Retro? Two reasons. First, if your mill doesn’t have a CSAFE port, you can’t use a GEM Retro. Second, the GEM Retro is a slave to your treadmill’s imprecision. The RUNN is what we used to ensure precise speed during Anthony Famiglietti’s 8:24 two-mile at Endurance Exchange. The RUNN is, first, just more precise, and second, a device you can calibrate.

      +

      Now, the calibration is, as well as I can tell, a way to make a misbehaving RUNN conform to whatever the treadmill says. In other words, in a pinch you can “calibrate” your usually precise RUNN to match your imprecise treadmill, so that they’re each equally imprecise. But I believe there’s a workaround, which is, if you have a really accurate measuring wheel you could measure, say, how many feet your treadmill really travels when the measuring wheel has traveled a quarter mile (1320 feet). The way the RUNN calibrates is that you set your treadmill at 5mph, you go through the RUNN’s calibration protocol, and the RUNN basically stipulates that what your treadmill is delivering is really 5mph. However, if you go through your measuring wheel exercise, and you do the math, and you actually run your treadmill at, say, 5.1mph or 4.9mph, because you know this really is 5mph, then you’ll have super-calibrated the RUNN. There would not, as far as I can tell, be a more precise measure of your treadmill’s speed.

      There is a firmware upgrade that is not factory installed into the RUNN that you receive. This adds the ability to pair North Pole’s own heartbeatz Apple Watch app to allow the Apple Watch to share HR data through the Runn to apps like Zwift. There was also an issue with the Bluetooth Running speed and cadence service that presented on Suunto watches and some other devices. The update also resolves an issue of data dropouts with Zwift that a small segment of users were experiencing. The firmware is in beta. I will find out more about this, and how you might access that firmware update. (I have not yet updated the RUNN unit that I have.)

      It may turn out that I’m dead wrong on some elements that I’m writing above. As a consequence, I may well need to add some edits to what I’m writing above. If I do, I’ll place a note here at the bottom (if not also at the top) of what I’m writing here, telling you what I’ve learned, and of any edits I make post-publication. Also, there is an existing thread on our Reader Forum on the RUNN and I’ll post any updates to that thread.

      Here’s more on the RUNN, and you can buy this from the manufacturer, from Zwift, and from other resellers. If you are interested in being a beta tester for the RUNN, where you'd get firmware updates that are in beta, you may email North Pole Engineering at support@npe-inc.zendesk.com

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/The_Smart_Bike_as_a_Home_Fit_Tool_7687.html b/site/slowtwitch.com/www/Indoor_Training/The_Smart_Bike_as_a_Home_Fit_Tool_7687.html new file mode 100644 index 0000000..644b36c --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/The_Smart_Bike_as_a_Home_Fit_Tool_7687.html @@ -0,0 +1,650 @@ + + + + +The Smart Bike as a Home Fit Tool - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      The Smart Bike as a Home Fit Tool

      + +
      +
      +
      +

      By waxing enthusiastic about smart bikes earlier over winter I stuck my neck out some. I touted the utility of these bikes as personal fit bikes, because of their ease and range of adjustability. They’re not the sorts of bikes a fitter would have in his studio, but each is a bike that you and I could use to tinker with our positions.

      I decided to put my position where my mouth is, and constructed an experiment to see how this might actually work. I had a Wahoo KICKR Bike in my studio for a while, and I’ve had a Tacx NEO Bike for the whole indoor season. What I intended to measure was my road position, which pretty much is my gravel position. The only difference difference greater than a centimeter between the two is my drops position, which is shallower on my gravel bike because of the bars I’m now riding. (In fact, I like this position so much better, I’m probably going to put a gravel bar on the new road bike I’m building.)

      +

      The experiment was this: Let my stationary position find itself. Raise and lower the saddle by feel; same with setback; same with cockpit distance and so on. I omitted measuring anything on these indoor smart bikes I was riding, so that I wouldn’t bias the outcome. When I knew I was done tinkering, my intention was to measure them, and see if my indoor position informed my actual outdoor bikes. If there was a variance, which position would I favor for all my riding?

      First, let me tell you what I measured (which fit coordinates). I’m using a pic of a Cervelo R5 to illustrate. I’m measuring:

      +

      Saddle height and setback: I’m using the same crank length on all bikes, 175mm, so, I can measure from the BB.
      Cockpit distance: both BB to Web X/Y, and also saddle nose to Web X/Y, because if there’s a variance in BB to Web X/Y then it might be that my whole cockpit is just shifted fore or aft. And, really, what I'm measuring to in this case is just Web X: the horizontal distance from either the saddle nose or BB to the hood trough.
      Handlebar elevation: I measure the difference in height between the top of the saddle and the top of that 31.8mm section right next to the stem on the handlebar.

      What is Web X/Y? It’s where the web of your hand contacts the hoods, which is at that point (ideally) where the hood makes its abrupt upward turn. It’s the center of the radius of that turn. I measure it with a quick lace like, say, Lock Laces: an elastic shoelace with a lace lock. I place this around the hood, as pictured below, and measure to the centerline, between the two hoods. I add saddle setback to the BB-to-Web X total to get the overall cockpit distance.

      +

      The Smart Bike numbers are from the Tacx NEO Bike I’ve been riding. Here’s what I got.

      First, the overall cockpit distance:

      Saddle Nose to Web X
      Road bike: 680mm
      Gravel bike: 670mm
      Smart bike: 675mm

      My gravel bike is what I’ve been riding lately, when I ride outdoors. About a month ago I felt as if I needed to lengthen my cockpit on that bike, so I replaced a 110mm stem with a 120mm stem and it felt better for sure.

      Last week I got around to measuring everything, and you see the results. I've got a cockpit distance of 670mm on the gravel bike, close to what I found I was riding on the Tacx NEO Bike. The outlier here is my road bike, which is a bit longer. I now must make a decision here. What am I going to do about this? One decision I’ve made is that I’m going to err toward a slightly shorter bike frame when I choose my new road frame. My current road frame has a frame reach of about 400mm, and I think I’ll move to something like 390mm or thereabouts.

      +

      BB to Web X
      Road bike: 600mm
      Gravel bike: 590mm
      Smart bike: 590mm

      There's a number of ways to measure this. Bear in mind that this is not HX I'm measuring, which is BB-to-handlebar clamp, and one typical way to do this is to use the SICI tool, pictured above. I'm measuring past the handlebar clamp and all the way out to the Web X, which is to those Lock Laces.

      There again, the road bike is the outlier, slightly. Why? Mostly I think it’s because the road bike is set up for climbing. When I’m out of the saddle I like a longer BB-to-Web X. Almost everybody does. This is one reason the Wahoo KICKR Bike is a slightly better smart bike for dialing in your fit: It inclines. This allows you to choose your BB-to-Web X both while riding level and while riding uphill, especially out of the saddle. Because I don’t ride out of the saddle as much on my gravel bike, that bike is optimized for seated riding, as is my Tacx NEO Bike. This is the one difference that might remain between my road and gravel bike: I might go 10mm further out on my road bike just for out-of-the-saddle climbing.

      +

      Saddle Setback from BB
      Road bike: 80mm
      Gravel bike: 80mm
      Smart bike: 85mm

      This semi shocked me. I spend all this time on that Tacx NEO Bike riding level (no climbing), in structured training and in high-intensity group rides where cadence is relatively high (almost always between 85 and 95), and I was dead sure my saddle would be a bit pushed forward on the Tacx NEO Bike. Basically, I’ve been doing what amounts to crit riding on that bike, just, indoors. However, I’ve also been thinking about this little experiment of mine while doing that riding, make sure that my weight displacement, saddle-to-hands, was right. Of all my bikes my position on the Tacx NEO Bike feels best, but that’s largely because there’s only one thing I’m doing: Riding flat; riding for an hour; high cadence; no climbing; very little riding out of the saddle.

      +

      Handlebar Elevation from Saddle
      Road bike: 105mm
      Gravel bike: 85mm
      Smart bike: 95mm

      As you see, I migrated to a handlebar elevation on that Tacx NEO Bike that splits the difference between my two outdoor bikes. In retrospect, I probably just should ride my road bike with the bars slightly higher. When I get my next road bike (which is the next bike I’m getting, as my current bike is at least 8 years old), I’ll not require the frame stack to be as low as I originally had figured.

      +

      How This Informs My Road Bike Choices
      My current road bike, a first-gen Cannondale SuperSix EVO, has a frame stack/reach of 577mm x 399mm. It’s got a -17° stem, 120mm long, and it’s got a single 10mm spacer above 5mm top cap.

      I’ll probably go with a frame closer to 390mm worth of reach. I don’t think I can go higher than about 595mm of frame stack or I won’t be able to get low enough, and that’s if I ride the bike with a flat stem slammed atop a 5mm dust cover. I would probably ride a -17° stem on that bike. I could go with a -6° stem which, if also slammed, means I could go as low as 580mm or so of frame stack. Probably that 580mm to 595mm is a good workable range, lots of bikes in that range, and something between 390mm and 395mm is pretty good for a frame reach.

      I feel pretty good about my experiment. I think my indoor cycling informed my outdoor bike positions, both the gravel bike (which needed a longer stem) and the road bike, where my indoor riding has convinced me my road bike position is 10mm or 15mm too low.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/The_Treadmill_Revolution__5581.html b/site/slowtwitch.com/www/Indoor_Training/The_Treadmill_Revolution__5581.html new file mode 100644 index 0000000..09593e5 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/The_Treadmill_Revolution__5581.html @@ -0,0 +1,631 @@ + + + + +The Treadmill Revolution? - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      The Treadmill Revolution?

      + +
      +
      +
      +

      Stationary training on Slowtwitch is almost all about the bike, not the run. It's not because you don't run on treadmills. We polled you less than 2 months ago, and 72 percent of you run routinely on a treadmill. More than 4 in 10 of you own one, and 1 in 10 are now in the market to buy one (about a third of you use the treadmill in your gym). You guys are treadmill runners.

      +

      You prefer the lonely life of the long distance treadmill runner while you're all about the party life of the social stationary cyclist. Is that it? If not, then we're not talking about the social ecosystem of stationary running because there isn't one to talk about.

      +

      + +

      + +

      This is coverage article number-2 in my series on ecosystems, which is what animated my noggin as I walked the CES show last week. I am the de Tocqueville of the microelectronic world, the outsider looking in and writing my impressions. de Toqueville did write that History is a gallery of pictures in which there are few originals and many copies. Does the treadmill market lack a vibrant ecosystem for defensible market reasons, or because it's a market of few originals and many copies?

      Here is cycling's stationary ecosystem: You have a Wahoo Kickr, or a Tacx NEO, or a Kinetic or a CycleOps trainer. You've got a subscription to Sufferfest, Zwift, TrainerRoad, VirtualTraining. You might need a Wahoo Speed and Cadence sensor or a power meter to drive your virtual training or racing software. You've got all of this hooked up via a computer or a smart phone and you're viewing these dynamic graphics on a big screen TV. Lots of interdependent stakeholders in this ecosystem.

      What about running? The treadmill itself, the motor, belt, frame and so forth, is the meat of the product. Most treadmills have consoles and this is harsh but it seems to me they're obsolete before they're ever introduced. The graphics screen might not be much larger than the size of your car's navigation system and how far will graphical interfaces advance during the lifetime of your treadmill, with you not sharing in the advancements because you're tied to the console's graphics?

      +

      + +

      +

      There is a small stationary running ecosystem. One stakeholder is VirtualRunner. Imagine a guy on a Segway taking off just before the start of a big race, like the wheelchair athletes. He's filming the course. It's a closed course, because runners are going to be coming by shortly. Barricades. Spectators. A treadmill runner can run this course, and if he's got a footpod the video of the course speeds up or slows down according to his own footpod speed. What is not done in cycling: you can buy an entry to the race. I could enter Falmouth Road Race, pay a fee, get the T shirt and whatnot mailed to me. Or, just virtually run the course as often as I want and “bandit” the race every time.

      You can also run scenic courses, like the Arch 5k. Above is a screen grab from the video. Just, as regards the graphics, you're always alone. What you see with VirtualRunner in a race is what the leading Kenyan sees.

      +

      Just spitballing, but let's imagine the console gone from the front of the treadmill, replaced with a big TV screen and cycling-quality graphics. How might this work? What I hear repeatedly is that no treadmill company's lawyer will allow its treadmill to be made a slave. That's what I'm asking for, a slave that obeys the commands of a preprogrammed run. I can see their point. Remember the I Love Lucy Candy Factory episode? Candy rolls by on a conveyor belt? Instead of the treadmill as a slave to the program, the runner becomes the slave. If you have a pre-programmed intensity during a cycling workout, yes it gets harder, maybe too hard, but you can always just stop pedaling. Not so the motorized treadmill. Flying out the back, that's the liability these treadmill companies envision.

      But there is a manual override. And the kill button. Wouldn't these mitigate the risk of injury? I don't know. This isn't my industry and the treadmill companies know their businesses. Still, what I'll write here is what I saw while walking the halls of the CES show.

      +

      + +

      +

      See this ethernet cable running in and around this treadmill? It's from Wahoo Fitness and it's called GymConnect. According to Wahoo Fitness, this will enable gym operators to easily create a wirelessly connected gym environment enabling gym goers to better track, manage and share their critical workout data for improved training and performance. It harvests data from the treadmill, to port into a training log. “The unintended consequence,” according to Wahoo's Mike Stashak, “was the massive interest in sending data the other way. We always knew we could do it but the interest in it far exceeded expectations.” What users want is a Borg Implant, to hijack and control the treadmill. This unit already has 2-way functionality. Wahoo is now a stakeholder in what I think might be just the frontier of a treadmill ecosystem.

      The image below is of a non-motorized treadmill, probably not one that a serious runner would select. What's cool about this is the graphics. There is a “handlebar” on this treadmill. With a trigger. This works with any computer game. It takes the place of a computer game's console or joystick. If you walk on the treadmill you make forward progress in the game. If you don't walk, no forward progress (but you can still shoot stuff). Turn the handlebar left or right, pull the trigger, you can zap Martians with a laser, kill gang members in Chicago, whatever. This is a real product, but not a real fitness product. What I see is proof-of-concept for a future fitness product.

      +

      + +

      +

      The image highest above is a non-motorized treadmill that is part of the Valencell display and in a future installment we'll talk about this interesting company. The treadmill has nothing to do with this company's product. It's just a way for a lady to walk outfitted with wearables powered by Valencell's high-tech, miniature, metrics-generating circuits. Just, note that this treadmill is unpowered, has no console, and if you did want to virtual race or train with it you won't fall prey to what happened to Lucy Ricardo. As long as the treadmill measures speed – or if the runner is wearing a pedometer or footpod or wearable exporting other metrics – he or she qualifies for a virtual session with first-rate graphics.

      I've traded a number of emails with the folks from Woodway. They make really nice motorized units, and non-motorized treadmills like Woodway's Curve (below). I can't speak for them. I just get the sense this company and probably others like it are looking hard at breaking out of the current paradigm. (Woodway has in fact had the capacity to do much of what I'm writing about for years; it just hasn't yet been widely deployed or championed.)

      I shot off an email to Jon Mayfield, founder of Zwift, and asked could he replace the cyclists with runners. Without going into detail or betraying confidences, Zwift is technically capable of activating on a running platform should it so choose. “The problem is that group running isn't really a thing people do, is it?” he asked me. “You mention you think a group run would be good; do you really believe that?”

      “You're asking me whether I'd like to drop some motherblanker like a bad transmission, or blow a hole in my lung trying?  Is this a trick question?”

      +

      + +

      +

      Jon then remembered he was talking to a 1-percenter. I and my ilk will seize any opportunity to compete. “What about the other 99 percent?” he asked.

      I countered with this: What if “keeping up” in a Zwift session meant maintaining a metric other than speed? What if staying with the pack meant running in a particular heart rate band? Or a training zone? This means people across a city, across a family, any group of varying abilities, could engage in a virtual group run or walk, and falling behind simply means you're dogging it even by your own fitness standard. I could even imagine group workouts in a treadmill studio, where everyone is in the pack, on the big screen, and staying in the pack means progressing at your appropriate rate.

      Zwift already supports voice-activated chat using Zwift Mobile Link on an iPhone.  Hit Group Chat and the regular morning walk or jog group includes not just the interactive, real time group session but with communication. Imagine runners on treadmills, earbuds filled with music, voice activated text on the screen, and all runners are in the virtual group just by staying true to your own personal physical ability. You can't even do all that in an analog, actual-group run.

      +

      + +

      +

      This could be executed on a treadmill with or without a motor. If you really wanted to virtual race you probably would need to restrict this to a footpod, otherwise some wiseacre could set the treadmill at 14mph and his treadmill is keeping up with the Kenyans while he's sitting on the couch watching Judge Judy.

      For treadmill running I can't currently find anything better than VirtualRunner. Could and should we expand the options? Easy for me to say, but, with very little work what we could have is Zwift for running, with the “pack” consisting of those racing or those who simply want to walk or run with a common personalized effort, using either a footpod with real-time exportable data or a Wahoo GymConnect. Ditch the console in front of the treadmill, replace it with a big screen. Presto. Ecosystem.

      Will the treadmill industry remain wedded to the idea that treadmill runners prefer to run alone? Would a treadmill company executive feel comfortable selling a closed system if his competitor just announced the part he plays in the community of device stakeholders in the new stationary running ecosystem? What if a treadmill company focused on making an excellent treadmill while inviting graphics and gaming folks, coders, and graphical display makers to bring their expertise to bear?

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Virtual_Week_May_20-27_2019__7278.html b/site/slowtwitch.com/www/Indoor_Training/This_Virtual_Week_May_20-27_2019__7278.html new file mode 100644 index 0000000..ae036f9 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Virtual_Week_May_20-27_2019__7278.html @@ -0,0 +1,644 @@ + + + + +This Virtual Week (May 20-27, 2019) - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Virtual Week (May 20-27, 2019)

      + +
      +
      +
      +

      Last week was a first for me. I set up a second Zwift station in my workshop and my buddy did his first Zwift ride, the Hilly Vanilli. As I learn more about Zwifting, get better at writing workouts, I’m getting more (not less) enthusiastic about Zwift in particular and stationary multiplayer platforms (that also include BKOOL and Rouvy) in general.

      Tuesday, 6:00am Eastern, we have a great ladies-only group, Ride of the Valkyries, Kelly Burns Gallagher will lead most of the time during the Summer months. This week, the all-new workout is Road Trip. Kelly is gauging the group’s threshold for stress, rest, and commentary, and every week we get a little closer!

      +

      Tuesday afternoon, 4pm Pacific (7pm Eastern) it’s Factory Work, another all-new workout. It’s so named because the profile of the workout in my workout building editor looks like a bunch of buildings with smokestacks. And (as a double entendre) the workout is designed to go with all the other Tuesday workouts to build, over time, little by little, a successful foundation. Factory work.

      See that link? Factory work above? Click it! Click it now! Then click the + sign and it’ll turn to a checkmark and you’re entered. Likewise with any of these events you think you’ll do. Then you don’t have to wonder whether you remembered to enter them as you’re hustling to make the ride before it leaves.

      Remember, both these Tuesday workouts are “structured training” workouts, which means as long as you keep pedaling you can’t get dropped, and you can’t drop anybody. Everyone rides together, according to his or her ability.

      Thursday morning, 5:35am Eastern, is the very popular Slowtwith Rise & Grind. On this ride you can get dropped! But we have several categories, and you join the group that suits your abilities (A, B, C or D, with D the slowest – that’s my group).

      Last week the Hilly Vanilli was contested over the Giro d’Italia’s TT course (twice in succession as it turned out). This is also a ride in which you can prevail or fail, and in this rare case the D group was better at staying together than either of the 2 faster groups. The D group starts at 3:55pm Pacific (6:55pm Eastern), followed 5min later by the C group, and 5min later by the B group. The rides all end at the same time, so, the D group rides 70min, C group 65min, B group rides an hour. The idea is to stay away from the group chasing you, and/or catch the group in front of you. It’s touch and go every week. A premium attaches to working together. I ride my tri bike on the trainer, but I’m virtually riding (the choice is made in Zwift) a road bike, because the draft works if you’re on a road bike; it doesn’t if you’re on a tri bike.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Virtual_Week_Slowman_Writes_His_First_Zwift_Workout_7231.html b/site/slowtwitch.com/www/Indoor_Training/This_Virtual_Week_Slowman_Writes_His_First_Zwift_Workout_7231.html new file mode 100644 index 0000000..c86f7a2 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Virtual_Week_Slowman_Writes_His_First_Zwift_Workout_7231.html @@ -0,0 +1,644 @@ + + + + +This Virtual Week: Slowman Writes His First Zwift Workout - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Virtual Week: Slowman Writes His First Zwift Workout

      + +
      +
      +
      +

      As I lurch toward the 20th Anniversary of Slowtwitch (which we’ll celebrate near the end of June), I contemplate our successes, failures, hallmarks and false starts. Here’s another one. Another what (success or false start) I can’t yet say.

      In either case here it is, This Virtual Week, and the idea is to tell you all what we as a community are doing virtually, which in the great majority of cases will mean on Zwift. New rides, new runs, what courses, what pros or other guest leaders are leading which events. Let’s get started.

      I’ve written my first Zwift cycling workout. I’m leading Tuesday Structured Training, which’ll take place at 7pm Eastern, 4pm Pacific. Two things of note: 1st, it’s my debut as workout writer, and we all may finish this thing not sufficiently tired, or we may all be force to give up partway through because it’s too hard (I have not yet pre-ridden my own workout); 2nd, this workout is called Atlantic City Wattbuilder. The reference is to the Atlantic City 70.3 and in the weeks forward-going you’ll see a number of workouts named for, and/or made with, specific events in mind.

      +

      In truth, Atlantic City Wattbuilder could be Galveston Wattbuilder, or Malibu Wattbuilder. But I told my friend Stephen Del Monte that in honor of the successes he’s building on the Jersey Shore (Escape the Cape, Women’s Philly Tri), I’d write Zwift workouts that some of his customer-athletes might use as preparation for his races. I hope Atlantic City Wattbuilder works the way I intend.

      Kelly Burns Gallagher will guest lead Ride of the Valkyries (Tue morning, 6:00am EST), giving our usual leader, Leslie Knibb, a breather.

      The Slowtwitch Rise & Grind takes place Thursdays at 5:35am EST, as usual, it’s for all levels; and Thursday afternoon's Hilly Vanilli (send-offs at 6:55; 7:00, and 7:05 pm EST) remains my cult-of-one favorite.

      Speaking of ride leaders! I’m going to start profiling some of our ride leaders. I have one, in particular, in mind that I’m going to start with. If you’d like to be a ride leader, I’d like to hear from you.

      And finally, I’ve built my first Zwift run workouts. But we don’t have Zwift runs on the schedule because I need about a dozen run leaders to commence and I have 4. If you run on a treadmill, and are free Tuesdays, first half of the day (we have 3 leave times) to help lead these, I’d sure love it if you got in touch with me.

      Sign up if you're to plunge into Zwift. Ask Zwift questions here if you'd like some answers. One final hint: click the links above now, check the box, you're entered, and then you won't have to remember to enter later in the week.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Virtual_Week_on_Slowtwitch_7265.html b/site/slowtwitch.com/www/Indoor_Training/This_Virtual_Week_on_Slowtwitch_7265.html new file mode 100644 index 0000000..422f1a0 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Virtual_Week_on_Slowtwitch_7265.html @@ -0,0 +1,645 @@ + + + + +This Virtual Week on Slowtwitch - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Virtual Week on Slowtwitch

      + +
      +
      +
      +

      First, it should be noted that the Washington Post, yesterday, reported on how your "dreadmill" runs get a lot more interesting when you Zwift them up.

      +

      Congratulations to the platform that beta'd right here on Slowtwitch for getting top billing on WAPO on how to clean out your arteries and have fun doing it!

      Here? We think we've got some good stuff for you. Kelly Burns Gallagher will lead the Valkyries tomorrow morning at 6am Eastern. It's "Ramp Up Your Fitness!" Little double entendre there. Should give you a hint as to the dominant features in that workout. Just moderately difficult. Women only.

      I will lead Slowtwitch Structured Training Tuesday afternoon, 4pm Pacific, the workout is "Gladly Pay You Tuesday." No ramps in that one. Just waterboarding interspersed with periods of hypoxia. More than moderately difficult, less than extremely difficult. This and the Valkyries above are no-drop.

      Slowtwitch Ride & Grind, Thursday, 5:35am Eastern, great group last week from what I hear. We're trying something new with the B group, because we're on the Giro TT course! The group will all ride together 'til the base of the climb. We'll then have B leaders go like heck, one fulgaz, one holding 4w/kg, the caboose at 3.5w KG. Everyone regroups at the top and try again on round-2, assuming we get a round-2.

      A gentle reminder to you all: "Riding" a TT bike (in the game) is a poor choice when you're on a ride where you can either drop folks or get dropped. Me, I'm actually riding a TT bike when I Zwift, but in the Game I choose a road race bike, for those sorts of rides. Speaking of which...

      +

      Hilly Vanilli is a "catch" ride, where groups leave at 3:55pm, 4:00pm, 4:05pm (Pacific), and each group tries to say out front, or catch the group in front. You're advised to choose a road race bike from the Zwift equipment menu.

      I have already clicked those links above, and clicked the little + sign, thereby "entering" the event. Took me 5 seconds per event to go there, enter, return back here.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_A_New_York_State_of_Mind_7581.html b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_A_New_York_State_of_Mind_7581.html new file mode 100644 index 0000000..501df9d --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_A_New_York_State_of_Mind_7581.html @@ -0,0 +1,645 @@ + + + + +This Week on Slowtwitch Indoors: A New York State of Mind - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Week on Slowtwitch Indoors: A New York State of Mind

      + +
      +
      +
      +

      No matter which online forum you frequent (hint: we'd really like you to be a member right here ), discussion of Zwift's New York world quickly devolves into the "love it" and "hate it" camps. Whether it's the difficulty of the KOM, the rolling nature of the loops around Central Park, or the frequency it appears in Zwift's calendar, it does not inspire indifference.

      Your faithful Zwift team here at Slowtwitch tends toward the "love it" camp, if only because it's so different relative to the other worlds. It was high time to host a bunch of our events in New York, including our 100/100 Challenge Support Runs for the first time. It's a nice change of pace -- even if the weather should be featuring snow, sleet, freezing rain, and 35 degree rain in game, if the actual weather is any indication.

      +

      Slowtwitch 100/100 Challenge Support Runs


      We're quickly approaching the end of the 100/100 Challenge. With less than 3 weeks to go, we decided to help give you a scenery shake-up. You can find the Shuman Trail Loop featured on Wednesday night's run, with even more changes coming next week. As of this writing, our 3 remaining support runs of the week are as follows:

      Monday, 7 PM Eastern
      Wednesday, 7 AM Eastern
      Wednesday, 7 PM Eastern

      +

      Slowtwitch Indoors Rides


      Ride of the Valkyries
      Our women's only structured training workout gets a respite from New York (and Watopia, for that matter) and instead heads to London. The goal, as always, is to keep the group together as you pedal through this week's workout, "Keep It Together." This hour-long workout features some strong intensity at the end -- be sure to save some strength.

      Tuesday Night Dynamite
      New name, same basic parameters! We decided our Tuesday night ride should join all of our other rides with a fun name. As always, this is a structured training ride that ensures the group stays together so long as you keep the pedals moving. The Everything Bagel circuit gives you a little bit of everything, as does this week's workout "Bring the Pain."

      Rise & Grind
      The early morning crew should see a full slate of ride leaders and sweepers back in action after what could only be described as a series of unfortunate events: illnesses, work trips, power outages, and more. We'll be tackling the Park Perimeter Loop on Thursday morning. As always, rides are fenced with the exception of pre-announced sprints, KOMs, and the final 10 minutes where the gloves come off.

      Hilly Vanilli
      Last and certainly not least, our chase-format event goes to everyone's other least favorite location: Richmond. The UCI course gives a lot of opportunities for groups to come apart -- be ready and willing to help your fellow group mates out so you can either chase down a group in front or keep a group behind away. If a group is caught, fences will usually be deactivated so that you can try to keep up with the category that has caught up.

      Remember: thank your ride and run leaders, and have fun!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_All_About_the_Hills_7501.html b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_All_About_the_Hills_7501.html new file mode 100644 index 0000000..955a25c --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_All_About_the_Hills_7501.html @@ -0,0 +1,648 @@ + + + + +This Week on Slowtwitch Indoors: All About the Hills - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Week on Slowtwitch Indoors: All About the Hills

      + +
      +
      +
      +

      Head to the Drop Shop and pick up the lightest equipment you can find. We're heading for the hills for most of our rides on Slowtwitch Indoors this week.

      +

      Ride of the Valkyries
      Our women's only structured training ride will head to Watopia for a trip around Sand and Sequoias. This week's workout is the aptly named "Race Day," featuring short bursts of high power followed immediately by some threshold work. As always, if you find the the ride to be a bit too caliente for your taste, you can always use the Zwift Companion app to turn down the bias. For more details on how to do that, read this article.

      +

      Slowtwitch Structured Training
      Our original Slowtwitch Indoors event is headed to New York City. We'll be on board the Knickerbocker route for this one. The workout is "Over Ones," featuring a steady ramp up in intensity until the final few minutes before the cooldown. Be ready: having ridden this workout before, those last few minutes are not for the faint of heart!

      +

      Rise and Grind
      Let's call this one the Turkey's Revenge. On Thanksgiving morning, brace yourself for the Bologna TT course. Groups will stick together until the base of the final climb; beacons and sweepers will hold a steady effort and will otherwise let people fly. Look to regroup on the descent and ride back to the start, then do it all over again. The B group should be able to make two full ascents in time. Be sure to register for the correct group: fences will be activated during the flats to avoid incorrectly categorized riders from blowing up their respective groups.

      +

      Hilly Vanilli
      Our weekly chase format event is headed to Yorkshire for the Royal Pump Room 8. Because of the undulating terrain, it will take plenty of teamwork in order to hit the finish line first. As always, the Ds are riding for 70 minutes, the Cs for 65, and the Bs go for 60 minutes of full throttle work.

      +

      What's Coming Next Month
      Starting next week, you'll be seeing some weekly runs as part of our 100/100 Challenge. We're also looking at revisiting some classic routes and workouts in order to push through the first half of the 60 Days of Darkness. See you in Watopia!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_And_Now_We_Run_7515.html b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_And_Now_We_Run_7515.html new file mode 100644 index 0000000..1e2d995 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_And_Now_We_Run_7515.html @@ -0,0 +1,648 @@ + + + + +This Week on Slowtwitch Indoors: And Now We Run - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Week on Slowtwitch Indoors: And Now We Run

      + +
      +
      +
      +

      With the start of our annual 100/100 Run Challenge, we've decided to take to the virtual streets of Watopia. Our 100/100 Challenge Support Runs help you check the box: 30 minutes of running -- no more, no less. These runs have pace bots coded in and we'll be rotating through a variety of courses and Slowtwitchers helping out along the way.

      +

      These runs, dubbed the Slowtwitch Indoors 100/100 Challenge Support Runs, will be held weekly at:

      Monday, 7 AM Eastern
      Monday, 7 PM Eastern
      Wednesday, 7 AM Eastern
      Wednesday, 7 PM Eastern

      +

      Slowtwitch Indoors Rides



      After a few weeks of hills, we're headed to some flatter terrain for our Slowtwitch Indoors series. The work won't be any easier, though -- these workouts will continue to give you some serious bang for your training time buck.

      Ride of the Valkyries
      Our women's only workout will be on the Jungle Circuit -- the rolling resistance changes in-game shouldn't make much difference for our structured training events. This week's workout is dubbed "Keep It Together," which will be easier said than done. Be sure to have your FTP properly input into the game, and it might be a good time to do a calibration on that trainer.

      +

      Tuesday Structured Training
      Sand and Sequoias will be our course this week, giving some good visual variety to what's going to be a bit of a leg burner. The workout name is "Go Go Go," and you'll be feeling it by the middle of this one. I tested it myself late last week -- get those fans going and make sure you've got an extra towel or two handy.

      +

      Rise and Grind
      It's always full-tilt from the gun on our East Coast bracer, and this week will be no exception given the leader and sweeper combinations. This week, R&G will also be hosted on Sand and Sequoias. All groups will be using the fence in order to keep proper category segmentation, so be sure to register for the right group!

      +

      Hilly Vanilli
      Our weekly chase ride will also now be activating the leader fence for the C and D groups -- we're looking to keep these groups together for the majority of the ride. Given the double-draft, this will also be advantageous as you try to stay away from the killer B's. This week will be on Tick Tock -- the draft will be more important than ever given how flat the ride is. Good luck!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Explore_the_World_7564.html b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Explore_the_World_7564.html new file mode 100644 index 0000000..9045bcf --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Explore_the_World_7564.html @@ -0,0 +1,644 @@ + + + + +This Week on Slowtwitch Indoors: Explore the World - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Week on Slowtwitch Indoors: Explore the World

      + +
      +
      +
      +

      Our Slowtwitch Indoors events this week are globe-trotting across the virtual world. All of our run events will be taking place on the Watopia run routes of Chili Pepper, 11.1 Ocean Boulevard, That's Amore, and the 5K Loop, whereas we go to Innsbruck, New York, and Bologna for our rides.

      100/100 Challenge Runs


      Our 100/100 Challenge Support runs are continuing to gain steam. Keep looking for Slowman or myself on the evening support runs. Each run is scheduled for 33 minutes, ensuring you'll get at least the minimum in to satisfy your 100/100 Challenge needs.

      100/100 Challenge Run Times
      Monday, 7 PM Eastern
      Wednesday, 7 AM Eastern
      Wednesday, 7 PM Eastern

      +

      Slowtwitch Indoors Rides


      Ride of the Valkyries
      Our women's only ride goes to the Innsbruckring! As always, this is a nearly no-drop ride (just make sure you keep the pedals moving at all times). Yet again, it's another music theme for the workout title; What's This will have you asking yourself that question by the end of things. Also, a special reward to whomever can figure out where that reference is from.

      Structured Training
      Our other no-drop ride is taking place in New York along the Knickerbocker course. The ERG mode will help smooth out the lumpy nature of the NYC routes. Be sure to have an updated FTP before taking on this week's workout: Hold On is not for the faint of heart (or improperly calibrated).

      Rise & Grind
      Bring the climbing legs: we're going to Bologna. Our ride leaders are going to be Matt (B), Mark (C), and Shaun (D). We'll use the fence for the first trip up the climb to keep everybody together at a steady pace within the w/KG cap for the particular category, regroup, and then on the second trip take down the fence on the lead-in to our final trip up the climb. As a reminder: road bike if you want the draft, and be sure to register for the right category.

      Hilly Vanilli Chase
      Hilly Vanilli is also going to Innsbruck. As always, the D group will get a 5 minute head start on the C group and a 10 minute lead on the Bs. The C and D group will use the fence until they are passed by the chasing group behind. Our ride leaders request that you remember that this is not a race -- there are no winners, so registering for the appropriate category is of the utmost importance. This is also a double-drafted ride and, as such, you'll want to be sure to use a road bike on this event.

      See you in Watopia!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Exploring_Watopia_7647.html b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Exploring_Watopia_7647.html new file mode 100644 index 0000000..1a3c53d --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Exploring_Watopia_7647.html @@ -0,0 +1,644 @@ + + + + +This Week on Slowtwitch Indoors: Exploring Watopia - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Week on Slowtwitch Indoors: Exploring Watopia

      + +
      +
      +
      +

      We like to explore a fair bit around when we're on Zwift; after all, when you have regulars you like to give them a fair bit of variety when it comes to courses. That being said, we do also identify courses that are great for specific ride experiences; as much fun as our Rise and Grind is, for instance, it becomes less fun on Road to Sky. Alternatively, because of the banded nature of our structured training events, we can do some courses that are otherwise difficult to manage in a group; for instance, the Jungle Circuit.

      That all being said, we thought this would be a good week to come back to where it all began: Watopia. Zwift's original world has a circuit for everyone; from the flats of Tempus Fugit to the largest of climbs, you can get a bit of everything when you head to Watopia. It's also home to May Field, Zwift's running track, and all of our group runs (Monday and Wednesday, 7 AM and 7 PM Eastern respectively).

      There is, of course, an exception to this rule. But that's my own fault. (More on that in a minute).

      +

      Ride of the Valkyries
      Our women's only training group gets the fast and flat course of Watopia Flat. At 6.4 miles, we should get at least 3 laps in on this trip around Ocean Boulevard and the back half of the original Hilly Route. This week's workout is Over Ones; as you can probably guess, that means that there will be some seriously tough 60 second segments for you to power through on the route.

      Tuesday Structured Training
      The ride that started Slowtwitch Indoors, the Tuesday Structured Training ride heads to Watopia's Big Loop. If you're looking to earn your route merit badge, be prepared to put in some additional riding after the workout is over; at 26.6 miles, including the trip over the Epic KOM, it's going to take a little bonus time to finish it off. This week's workout is "The Bends," which is approximately how you should feel by the end of it.

      Rise and Grind
      Alright, so here's the exception to the Watopia rule this week. I blame myself. I'd originally planned on every ride being in Watopia, and I simply put London into the Rise & Grind slot. So if you don't like it, blame me. We're going flat and fast in London this week, as we go to the Greatest London Flat circuit. We will regroup after each sprint. Be forewarned; losing the draft on these flat courses is perilous. Your leaders this week are Alex (B), myself (C), and Shaun (D).

      Hilly Vanilli
      Our Thursday night barn-burner, this chase format ride will head for Watopia's Figure 8 course. Enjoy two trips over the original KOM, one in each direction. Teamwork is the name of the game! Whether you're a part of B, C, or D groups, you need to all pull together in order to either chase down the team in front or keep away.

      Casual Fridays
      Our first Casual Friday was a smashing success, with over 250 of you joining for this mash-up between Slowtwitch and Every Man Jack. This week, we'll take on Road to Ruins for our trip, once again with the leader beacon given to EMJ's Sean Hale. This week's workout is the aptly named Weekend Warm-up, which features just a couple of efforts to get you prepared for a weekend of either training or virtual racing.

      Have fun out there, and we'll see you next week.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Feeling_the_Burn_7544.html b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Feeling_the_Burn_7544.html new file mode 100644 index 0000000..421bc5e --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Feeling_the_Burn_7544.html @@ -0,0 +1,647 @@ + + + + +This Week on Slowtwitch Indoors: Feeling the Burn - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Week on Slowtwitch Indoors: Feeling the Burn

      + +
      +
      +
      +

      I have a specific approach that I like to take for any type of shopping trip: map out a plan, get in and get out. It's quick. Simple. Efficient.

      That tends to be the approach I like to take when I'm riding indoors on Zwift. Let's get in some intensity, some structure, and get the heck on out of there. Some of that is related to the fact that my "pain cave" is located down in the basement, and some of it is related to the fact that I'm frequently Zwifting while it's dark. That and I'm pretty positive I could recite to you Peppa Pig episodes in correct chronological order as it is the preferred entertainment of our 3-year-old daughter while Mom or Dad is on the trainer.

      +

      The point being: we're looking to give you the maximum bang for your buck on this week's Slowtwitch Indoors rides and runs on Zwift. In order to do that, we tend to crank up the intensity a little bit. If you're finding the salsa to be a bit caliente, you can always use the Zwift Companion app to turn down the bias a bit.

      +

      100/100 Challenge Runs


      Lord knows that I have failed miserably in my bid to complete the 100/100; crashing your bike hard the day before the challenge started will do that to you. The new goal is to shoot for 70.

      That being said, the Challenge is significantly easier when you've got motivated individuals joining you for a run. As of this writing, we've got 3 more runs available to help you keep the streak alive:

      Monday, 7PM Eastern
      Wednesday, 7 AM Eastern
      Wednesday, 7 PM Eastern

      +

      Slowtwitch Indoors Rides


      The riding this week are all rides of my creation, meaning if you have a grievance to air, forward it my way. I'm still figuring out the best balance on some of these rides, even though I do subject myself to them first before they enter the ride rotation.

      +

      Ride of the Valkyries
      Our women's only training ride this week heads to New York City! We'll be taking a tour of the Astoria Line 8. Our workout title gives away what you'll be doing: Pyramids and Sprints. Have fun!

      Tuesday Structured Training
      Our original ride continues to deliver a punchy experience. This Tuesday will also be featuring the New York City world, going for a trip around the Everything Bagel. Embrace your inner Clubber Lang and bring the pain, as that's the name of this week's workout.

      Rise and Grind
      Might we recommend waking up 15 minutes earlier and getting a bonus cup of coffee in? Given the return of Alex to the B group lead, this week's trip around Watopia's Waistband will be full gas from start to end. We should have full ride leaders across all categories this week, along with our traditional B group caboose (me).

      Hilly Vanilli
      We've received positive feedback after adding the fence to this ride, so expect to see it turned on for the majority of the event until it's really time to try to stay away (or catch the group up the road). This week should be a good opportunity for the Ds to stay away on the Out and Back Again circuit in Watopia.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Flat_and_Fast_7658.html b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Flat_and_Fast_7658.html new file mode 100644 index 0000000..248e741 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Flat_and_Fast_7658.html @@ -0,0 +1,644 @@ + + + + +This Week on Slowtwitch Indoors: Flat and Fast - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Week on Slowtwitch Indoors: Flat and Fast

      + +
      +
      +
      +

      We're now somewhere in our sixth week of the "new normal" of COVID-19. With that, we've noticed a big trend upwards in attendance in our Zwift events; over 1,000 of you joined us last week across our nine events, including our highest-ever total of riders for the Rise & Grind. Well done, all. Welcome to the party.

      This week, we continue our theme of heading back to our roots. Watopia is Zwift's original world, and although it frequently has the most riders in it, many routes are often overlooked. We're shining a spotlight on some of the lesser used routes this week that offers a bit of something for everyone over the course of their miles.

      +

      Ride of the Valkyries
      The women's only structured training ride, our Tuesday bunch heads to a favorite of occasional guest lead Kelly Burns Gallagher: Sand and Sequioas. This route has a bit of everything: punchy climbs, a long KOM, and a drag race through Fuego Flats. However, you'll be overlaying it with this week's training workout -- no matter if you crank out 1.0 w/KG or 4.0 w/KG, the group will stay together. Keep those pedals moving and have fun.

      Tuesday Structured Training
      The original structured workout ride for our Slowtwitch Indoors series, it's the ride you'll most likely find Dan Empfield on throughout the week. Much like Ride of the Valkyries: everybody stays together so long as you keep the pedals moving. Drop out of ERG mode, dial down bias, whatever you've got to do to keep the pedals turning over. This week it also explores Sand and Sequioas.

      Rise & Grind
      The first of our two Thursday free-ride events, we'll be headed to Out and Back Again. This 26.1 mile route has a dash of everything: lots of flats, until it isn't. Climbing, until it goes back flat again. Staying with your group will be incredibly important as the double-draft effect really shows on these flatter courses. Be sure to be properly categorized. This week's ride team has Alex and Mark for the Bs, myself and Matt for the Cs, and Shaun leading the Ds. Over 250 of you joined us last week -- let's have some more fun this week!

      Hilly Vanilli
      Our only chase format ride, each group will spin for a specific period of time. The Ds will leave and ride for 65 minutes; the Cs ride for 60 minutes; the Bs spin on for 55 minutes. The goal? Finish all together in one big bunch at the end. This is another ride that's heading to Out and Back Again -- it's a route that should give the Ds a fighting chance to stay up the road from the Bs and Cs. Again, this ride is double-drafted: mind the gaps because once they open up, it gets tough to close back down again.

      Casual Fridays
      Our newest ride is a big hit! This Friday morning weekend-warm-up is another structured training workout; just keep pedaling and you'll stay banded with the big group. Our friends at Every Man Jack have begun supplementing workouts, which should dial down a touch of the intensity that you've seen the last couple of weeks. (When we say Casual, we mean Casual.) Come join us on Tempus Fugit: the fastest and flattest roads in all of Watopia.

      Slowtwitch Indoors Runs


      As always, we are on May Field for our three additional Slowtwitch Indoors runs of the week: tonight at 7 PM Eastern, and Wednesday at 7 AM and 7 PM Eastern, respectively. Each run is 30-35 minutes in length, and we all stay on the track together. May Field is a known hotspot for celebrity appearances on Zwift, including but not limited to defending Ironman World Champion Jan Frodeno. Join up and we'll see you in Watopia!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Go_Fast_Take_Chances_7575.html b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Go_Fast_Take_Chances_7575.html new file mode 100644 index 0000000..b92bd18 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Go_Fast_Take_Chances_7575.html @@ -0,0 +1,646 @@ + + + + +This Week on Slowtwitch Indoors: Go Fast, Take Chances - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Week on Slowtwitch Indoors: Go Fast, Take Chances

      + +
      +
      +
      +

      We're about 10 days off the back of Endurance Exchange, the largest gathering of industry stakeholders in our sport. Race directors, retailers, coaches, manufacturers, clubs, and brands all came together to learn lessons from presenters and each other.

      We all also had the pleasure of watching Anthony Famiglietti test his hand at the American record for the 2-mile on board a treadmill. After finally finding a treadmill that could hold pace (which, even that required a remote hack from Woodway HQ to allow it to run 15+ MPH), Famiglietti took to the treadmill at Saturday's lunch to give it his best.



      Famiglietti's final time of 8:25 (including a negative split) came up short of the record. But given his tenacious attitude with all of the fits and starts that came along with it, we can't help but be inspired. With that in mind, we're challenging everybody on our Slowtwitch events this week to push a little further outside of their comfort zone.

      +

      Slowtwitch 100/100 Challenge Support Runs


      We are quickly approaching the end of our 100/100 Challenge. We won't stop having runs at the end, though -- we'll just start changing what types of paces and workouts you see within a given event. This week, all of our runs still track within the 4 Watopia run-only circuits. Next week, we will begin traversing some of the New York City options for a nice change of scenery.

      Run Times
      Monday, 7 PM Eastern
      Wednesday, 7 AM Eastern
      Wednesday, 7 PM Eastern

      +

      Slowtwitch Indoors Rides


      We're going through a few of the greatest hits this week for our Slowtwitch Rides; as requested by some of our various ride leaders and frequent attendees, we're using some of our favorite workouts and routes to keep you entertained. We promise: it's not Groundhog Day, despite Bill Murray's new Jeep commercial.

      Ride of the Valkyries
      Our women's only ride heads to a favorite circuit of one of our ride leaders: the infamous Watopia Jungle Circuit. The no-drop, rubber-banded ride still has plenty of "Go, Go, Go" in it, as that's the title of this week's workout. You're permitted to groan at the prior sentence now. This all said, this is a strong ride with plenty of strong women. Get your coffee and get pedaling!

      Tuesday Structured Training
      Our other no-drop event, Tuesday night's ride heads to Watopia's Dust in the Wind (which, incidentally, also includes the Jungle). The workout "Highs and Lows," much like the circuit, features plenty of peaks and valleys. Be sure your FTP is accurate before attempting.

      +

      Rise and Grind
      Hi ho, it's to the Innsbruckring we go! This favorite of the Thursday morning crowd always gets the blood pumping. We'll usually take the first lap together before giving the sprint a chance and regrouping. A reminder that fences will be on (with the exception of the sprint and the final few minutes) and that leaders will look to average, over the course of the ride, the median of the w/KG range for the group you sign up for. Also, a reminder that although we do have designated sweepers, unless you call out for a wheel, you will likely get left behind.

      Hilly Vanilli
      The Innsbruckring is so nice, we had to do it twice this Thursday. Our chase format ride will also take to this course, which will suit the punchier members of the B and C groups. As always, our Ds will ride 70 minutes, the Cs 65, and the Bs 60, with the goal of completing roughly the same amount of distance by the end of the ride. Work together and have fun!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Introducing_Casual_Fridays_7639.html b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Introducing_Casual_Fridays_7639.html new file mode 100644 index 0000000..ae3803d --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Introducing_Casual_Fridays_7639.html @@ -0,0 +1,644 @@ + + + + +This Week on Slowtwitch Indoors: Introducing Casual Fridays - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Week on Slowtwitch Indoors: Introducing Casual Fridays

      + +
      +
      +
      +

      Ever since we expanded our Slowtwitch Ride portfolio to four events, they've all had one thing in common: they tend to be pretty hard rides. Whether it's the banded, structured workouts of Ride of the Valkyries or Tuesday Structured Training, or the mass group rides of Rise and Grind and Hilly Vanilli, they are hour-long efforts that are designed to put you through the proverbial wringer. After all, it's the hard efforts that people talk about after the fact.

      But equally important are the less sexy sessions. The so-called "meat and potatoes" sessions that prep you for those difficult ones. Those that warm you up for the bigger blocks ahead.

      Well, now you can do a session like that with us and Team Every Man Jack's Sean Hale each week on Friday, 6 AM Pacific time. Our Casual Friday ride is designed to prep you for a weekend of long training or racing. It's a mellower ride with some "opening" efforts to get you ready for whatever you've got planned on Saturday or Sunday.

      +

      As for the rest of our rides this week:

      Ride of the Valkyries
      Our women's only ride keeps seeing great activity. This week, we're going to one of the fastest courses in all of Watopia: Tick Tock. As a structured training workout, as always, just keep the pedals moving and you'll be sticking with the pack. This week's workout is the race preparatory Attack Attack Attack! You can probably guess what the ride is meant to simulate.

      Tuesday Structured Training
      We've reverted our name to it's original setting, but make no mistake, there's still plenty of firepower involved. This ride will also be taking to Tick Tock this week. Our workout of the week is inspired by a track from Pearl Jam's Vitalogy: Spin Spin. Get the fans and music on high.

      Rise and Grind
      Thursday morning's whip-cracking event is going to be a party this week. Well, it'll be an after party on the Volcano. We'll keep groups together for as long as we can before unleashing the fury. Our ride lead and sweep combinations this week are as follows: Matt and Alex for Bs, Mark and Ryan for Cs, and Shaun for the Ds. We had nearly 300 of you join us last week, and we'll be pushing to break that number this week.

      Hilly Vanilli
      We get back to business as usual this week: 55 to 65 minutes of hard riding, looking to either keep away or chase down the group up the road. We won't be racing up the Alp this week, either. Instead, you can find yourself on the Volcano circuit. The advantage of this course? Get dropped, stay pedaling until you get caught by the group behind, and re-join! It's a good way of testing your limits, so maybe it's a week to try one group up the road.

      Slowtwitch Indoors Runs


      We're still trotting away on May Field! Get your Runn sensors or footpods ready, and join us for any of our three remaining ST Indoors runs of the week: tonight at 7 PM Eastern, Wednesday at 7 AM Eastern, or Wednesday at 7 PM Eastern.

      We'll see you in Watopia. Have fun!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Let_s_Go_Racing__7628.html b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Let_s_Go_Racing__7628.html new file mode 100644 index 0000000..c3ea5af --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Let_s_Go_Racing__7628.html @@ -0,0 +1,644 @@ + + + + +This Week on Slowtwitch Indoors: Let's Go Racing! - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Week on Slowtwitch Indoors: Let's Go Racing!

      + +
      +
      +
      +

      We won't bury the lede: we're presenting our first-ever Slowtwitch Indoors race on Zwift this Thursday night, in our Hilly Vanilli timeslot. For our first race, we also thought that we'd give you a taste of the finest climb that Zwift has to offer. So we're storming up Road to Sky: 12.4 miles, 3,753 feet of climbing. Please follow our category guidelines when signing up (understanding that, when climbing, you're likely going to be holding a higher average than you might otherwise find yourself in on a "normal" day). Bring your climbing legs, turn the fans up on high, and go.

      +

      In terms of our other Slowtwitch Indoors Rides:

      Ride of the Valkyries, Tuesday 6 AM Eastern
      Our women's only structured training workout sticks to Watopia's Waistband this week. Although everyone is banded together, there's still plenty of hard work to be done this week. This week's workout is "The Bends."

      Tuesday Structured Training, Tuesday 7 PM Eastern
      Another one of our structured training workouts, Tuesday's night time ride will have you headed to Richmond. Although the structured training element will take the sting out of the climbs over the second half of the course, the workout will still be a difficult ride -- you can thank Slowman himself for this week's workout.

      Rise and Grind, Thursday 5:35 AM Eastern
      This week's early-morning power exhibition covers a little bit of everything in London. We take on the Keith Hill After Party course. This should work out so that we keep groups together until the final climb, at which point the gloves will come off. This week, you'll have Mark and Matt for the B group, Alex and myself for the C group, and Shaun will head up the Ds. As a reminder: please register for the correct category! Ride leaders will look to average the middle of the w/KG range for their respective groups. If the fence is available, we do use it to zap flyers.

      Slowtwitch Indoors Runs


      We're still keeping everyone on May Field for the time being, with approximately 33 minutes of running all together and messaging. Yours truly will be trying to hop on for this Wednesday's 7 AM edition of the run. As always, you have (as of this writing) 3 more sessions you can possibly join this week: tonight (Monday) at 7 PM Eastern, Wednesday at 7 AM Eastern, and again Wednesday evening at 7 PM Eastern. Socially distancing doesn't have to mean going at your run alone -- it just means we're going to keep meeting virtually.

      We Noticed: Lionel Sanders and the Ronde Van Zwift


      Did you catch Ronde Van Zwift this weekend? Three laps of Richmond with cobbled climbs, 27.6km, Matt Stephens and Jörg Ludewig on commentary sounding just about the equals of Phil Liggett and Jens Voigt.

      Slowtwitch Forum favorite triathlete Lionel Sanders wiped the floor of Canyon Zwift CC
      and the rest of the pro cyclists. Ludewig is fluent in English but brings just enough German in to make for good copy. “Sometimes triathletes like to kick cyclists’ asses and
      Lionel is really doing this at the moment,” he said. It’s “very difficult to crash on Zwift, even for Lionel Sanders.” You’ll hear the unconscious dissing of triathletes during the webcast, but the announcers did lavish praise where praise was due. “People were
      laughing about him,” said Ludewig, but, “here we have the results.”





      It’s a short watch, very compelling, but if you really want to get to the meat of it, start watching about a half-hour in. Oh, and Canyon got the last laugh on this one. Yes, Lionel beat up on Canyon’s team of professional cyclists… riding his Canyon.

      Have fun in Watopia, everybody. We'll catch up next week.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_May_Field_Day__7585.html b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_May_Field_Day__7585.html new file mode 100644 index 0000000..b4a549c --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_May_Field_Day__7585.html @@ -0,0 +1,645 @@ + + + + +This Week on Slowtwitch Indoors: May (Field) Day! - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Week on Slowtwitch Indoors: May (Field) Day!

      + +
      +
      +
      +

      After moving our rides and runs around a fair bit last week and giving our Tuesday evening ride a new name, we're settling back into some normal routines here at Slowtwitch Indoors. All of our runs will be moving to a central location in Watopia, while our rides move into some familiar scenery this week.

      +

      100/100 Challenge Support Runs


      Into the home stretch now! With just two weeks remaining in the 100/100 Challenge, we thought we'd give you some inspiration for a little bit more speed. All of our runs will now take place on May Field. This gives you a chance to speed up or slow down as you see fit, all while keeping most of the group together throughout the run. As always, runs will take place on Mondays and Wednesdays, with options at 7 AM Eastern or 7 PM Eastern.

      +

      Slowtwitch Indoors Rides


      Ride of the Valkyries
      It's Yorkshire time! Our women's only structured-training workout heads over to the latest UCI addition to Zwift. In honor of heading to the UK for this ride, you'll be pedaling along to What's This, a workout inspired by Radiohead's "Paranoid Android." Yes, I'm a dork.

      Tuesday Night Dynamite
      Although we recommend road bikes for all of our Slowtwitch Indoors events, you will definitely be on the clock with this week's course selection of Tick Tock. The workout of "Hold On" features a lot of late intensity -- be prepared to go deep into the well tomorrow night.

      Rise and Grind
      Our Thursday morning bracer heads for the hills of Richmond. We'll hold a neutral first lap together before dropping the fence for sprints and KOMs on the second lap. The B group will likely get one last sprint in before calling it a day. Look for Matt, Mark, and Shaun as your respective B/C/D ride leaders.

      Hilly Vanilli
      The Thursday night ride sticks in Watopia, going Out and Back Again. We like this particular course due to the good mix of terrain. As always, the Ds begin 5 minutes before the Cs, who get a 5 minute head start on the Bs. Work together as a group, and hopefully we'll all end having covered roughly the same distance.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_More_Miles_More_Smiles_7555.html b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_More_Miles_More_Smiles_7555.html new file mode 100644 index 0000000..2f7b509 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_More_Miles_More_Smiles_7555.html @@ -0,0 +1,645 @@ + + + + +This Week on Slowtwitch Indoors: More Miles, More Smiles - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Week on Slowtwitch Indoors: More Miles, More Smiles

      + +
      +
      +
      +

      2019 was a banner year for our Slowtwitch Indoors stable of Zwift events -- we've seen rapid expansion to encompass 4 weekly rides and 4 weekly runs. We see consistent ridership across all of our events and love hearing about our riders successes back out on the roads in races.

      In 2020, we'll keep pushing the boat out across all of Zwift's worlds. What that will hold will be determined. But at the very least, it'll start with a full slate of events to kick things off.

      +

      100/100 Challenge Support Runs


      This week's runs have all been extended out to "33" minutes. The number is in quotes as, I think everyone has noticed, the stated time does not necessarily match the total run time. Because we want to be sure that every Slowtwitch Indoors run will count for the purposes of the event it is serving, we added a little bonus time to make sure your miles make it into the Training Log leaderboard.

      As always, our runs are at:
      Monday, 7 AM Eastern
      Monday, 7 PM Eastern
      Wednesday, 7 AM Eastern
      Wednesday, 7 PM Eastern

      +

      Slowtwitch Indoors Rides


      Ride of the Valkyries
      Our women's only structured training workout this week takes a lap around Watopia's Sand and Sequioas circuit. All of this month's structured training workouts will feature some type of musical inspiration, as the song sets the mood as I write the workout. For this ride, "Strange Part of Our Town" comes from an old single from The Strokes.

      Tuesday Structured Training
      Our original ride heads to the Jungle Circuit. Although you won't be going anywhere fast (either in-life or in-game due to the rolling resistance update), the group should stick together for 2 trips around the circuit. The workout is dubbed "Highs and Lows," with plenty of work avoiding the so-called Muddy Middle.

      Rise & Grind
      Our bright-eyed and bushy-tailed bunch of ride leaders eagerly await taking you through London on a thankfully flat-ish route this week. Don't take that as a signal the pedaling will be easy, though, as flat rides will frequently see groups coming apart at the midway point. Work together to use the draft as much as you can! Fences are active with the exception of sprints and the final five minutes.

      Hilly Vanilli
      The chase will be on early this week. With the Richmond UCI course, we'll see the Bs and Cs make moves each lap on the hillier second half to try and chase down the early-starting Ds. That said, though, if you make a break you'll want to bring company -- the flat sections see pack riding get significant benefit. Work together and have fun!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Race_Simulations_7495.html b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Race_Simulations_7495.html new file mode 100644 index 0000000..53c1a1c --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Race_Simulations_7495.html @@ -0,0 +1,645 @@ + + + + +This Week on Slowtwitch Indoors: Race Simulations - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Week on Slowtwitch Indoors: Race Simulations

      + +
      +
      +
      +

      There's two scenarios that compete in my brain for the title of "Hardest I Have Ever Ridden a Bicycle."

      The first is in cyclocross racing. The second is Zwift racing.

      Both call for an incredible amount of power at the start, holding on for dear life, and then surviving the inevitable attack at the first hill (whether you're having to run that hill is another question).

      +

      I am, by my own admission, terrible in both scenarios. I need to work on it. Based on conversations with fellow 'cross riders and Zwift racers alike, it sounds like we all could use a primer on it. So, with that in mind, this week on Slowtwitch Indoors features rides that will put your legs to the test.

      +

      Ride of the Valkyries
      Our Women's only ride this week will be all about the attack. If at first you don't succeed, attack and attack again! That said, as one of our structured training rides: so long as you keep the pedals moving, you'll stay with the group. You can also dial down the intensity of the workout by reducing the percentage of FTP the workout is based off of, if needed, within the Zwift Companion app.

      Tuesday Structured Training
      To put you in the proper mood for this week's ride, we're headed to the Richmond UCI course! This week this ride will focus on the efforts necessary to chase down a break late in the race. It'll force you to recover quickly after some very heavy efforts.

      Rise & Grind
      You're going to want to make sure the coffee is flowing before this ride. Although we're going to be on a flat course this time out, this is a ride that starts out hard and only cranks up the intensity from there. The double-draft is on, so fight like tooth and nail to stay with your group! Please be sure to register for the correct ride category, as we do turn the fence on to help keep groups together.

      Hilly Vanilli
      Our chase ride is all about trying to stay away or trying to catch the group up the road. The Ds ride 70 minutes, the Cs go 65, and the Bs are on the clock for 60 minutes. For this week, it's all on the Astoria Line 8; this will be a challenging loop that will force groups to work together to either stay in the breakaway (Ds) or to reel the breakaway in (Bs and Cs).

      An early preview of next week for all rides: be prepared for hills...

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Return_to_Watopia_7595.html b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Return_to_Watopia_7595.html new file mode 100644 index 0000000..18c6382 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Return_to_Watopia_7595.html @@ -0,0 +1,644 @@ + + + + +This Week on Slowtwitch Indoors: Return to Watopia - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Week on Slowtwitch Indoors: Return to Watopia

      + +
      +
      +
      +

      With the end of this year's 100/100 Challenge, we thought it'd be a good time to return to some of the basics with this week's Slowtwitch Indoors events. As such, every single ride and run will take place in Zwift's original world: Watopia. Although Zwift has expanded sections of the world over the years, it is still the only world available every single day (without having to world hack.) That being said, Watopia contains some of this writer's favorite routes. Those are all part of this week's Slowtwitch Indoors events.

      Slowtwitch Indoors Groups Runs


      Well, we couldn't keep calling them 100/100 Challenge Runs, now could we? Runs are scheduled for 33 minutes in each session, and all take place on May Field. No matter your pace, you are welcome to join us to get the mileage in! We also plan to add, in the near future, structured training runs that operate similarly to our banded structured training bike workouts. More to come soon!

      As of this writing, runs take place:
      Monday, 7 PM Eastern
      Wednesday, 7 AM Eastern
      Wednesday, 7 PM Eastern

      +

      Slowtwitch Indoors Rides


      Ride of the Valkyries
      Cue the Guns n Roses, because we're going to the jungle. Although the rolling resistance changes in-game have resulted in plenty of gnashing of teeth, it doesn't impact our structured training workouts. So long as you keep the pedals moving, you're going to stay with the group. This week's workout, Take it to the Limit, progressively ramps up the difficulty throughout the ride.

      Tuesday Night Dynamite
      The Jungle Circuit is so nice, we had to do it twice. Our Tuesday night structured training ride also will take a few laps around the now-scarcely used Jungle Circuit. This week's workout is dubbed "Spin Spin," written to a song from 1994. I may have included a bonus sprint or three in there. At any rate, just make sure you keep the pedals moving and you'll stay with the group.

      Rise and Grind
      Our Thursday morning event takes a trip around Watopia's Waistband to give you a taste of everything: flats, climbs, downhills, and sprints. Because Zwift has removed the fence for the time being, we ask that you register for the appropriate group and stick with your ride leader. Communication via the Companion app is key! A sweeper will be available for the B group only this week. Ride leaders will look to average the middle of the category's w/KG for the entirety of the ride (meaning it will be easier at the beginning, harder at the end). Have fun!

      Hilly Vanilli
      The Chase is on! This week, the Bs and Cs will try to close down the Ds on Tick Tock. Watopia's flattest route should give some high average speeds and an opportunity for the earlier starters to stay up the road. Similar to Rise and Grind, the fence is not currently available, so please be sure to register for the appropriate category and listen to your leader/sweeper on the road. Remember: nobody likes the guy that blows up the group ride.

      Enjoy your week on Zwift!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Second_Verse_Same_as_the_First_7560.html b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Second_Verse_Same_as_the_First_7560.html new file mode 100644 index 0000000..cf4b481 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Second_Verse_Same_as_the_First_7560.html @@ -0,0 +1,644 @@ + + + + +This Week on Slowtwitch Indoors: Second Verse, Same as the First - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Week on Slowtwitch Indoors: Second Verse, Same as the First

      + +
      +
      +
      +

      We're fast approaching the midway marker of the 100/100 Challenge. I have fallen off the wagon long ago, but our faithful participants continue to log dutifully. Over 250 runners have at least 25 runs to their name so far, and nearly 100 are yet to miss a day.

      Our weekday Challenge support runs continue to traverse the run-specific tracks of Watopia. Be on the lookout for a potential Slowman sighting during the evening sessions.

      100/100 Challenge Support Run Schedule
      Monday, 7 PM Eastern
      Wednesday, 7 AM Eastern
      Wednesday, 7 PM Eastern

      +

      Slowtwitch Indoors Rides


      Similar to the run schedule, we're spending a lot of time in Watopia this week. The only exception is our Tuesday Structured Training event, which will be headed to London.

      Ride of the Valkyries
      Our women's only ride gets to experience last week's Structured Training workout, Highs and Lows. This one will scorch the legs in more ways than one -- you'll be pedaling the Whole Lotta Lava course. Be sure to get the coffee on board before riding!

      Tuesday Structured Training
      Off to London with you! Our Tuesday night group event will trundle along the flat roads in London. For a workout, you get to experience the Strange Part of Our Town. (Although, I suppose, if I were being correct to the source material, this ride should be in New York. I digress...)

      Rise and Grind
      Our early morning East Coast barnburner heads to Dust in the Wind this week. Be very smart to stay in the draft, or else you will be the dust in the wind and left far, far behind. As always, we please ask that you register for the correct group -- flyers will be fenced out. Pedal hard and often!

      Hilly Vanilli
      Last but not least, this week's Hilly Vanilli will also take a trip up and around the Volcano on Whole Lotta Lava. This should let users who get dropped the ability to latch back on as the groups head back around for another trip. This one is definitely going to favor the climbers in the group -- good luck and have fun!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Slowtwitch_Staff_Thursday_7664.html b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Slowtwitch_Staff_Thursday_7664.html new file mode 100644 index 0000000..d3850dc --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_Slowtwitch_Staff_Thursday_7664.html @@ -0,0 +1,644 @@ + + + + +This Week on Slowtwitch Indoors: Slowtwitch Staff Thursday - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Week on Slowtwitch Indoors: Slowtwitch Staff Thursday

      + +
      +
      +
      +

      Slowtwitch has long been an evangelist for Zwift. One doesn't need to look much further than the birth of Zwift in our Reader Forum to know that we really, really like Zwift.

      Although you can frequently find us in riding and running around in Zwift via the Companion App, it's rare to find a dedicated time where you can run into some of the familiar faces and names from here at Slowtwitch. We thought we might change that a little bit. So, at least for this week, you can choose one of our two Thursday rides and just about be guaranteed a hello from one of us here at Slowtwitch, in addition to our ever-present ride leaders. Let's get down to business: here's what to expect if you join us in one of our events this week.

      +

      Ride of the Valkyries
      Queue up the Guns n Roses, ladies: we're going to welcome you to the Jungle Circuit this week. We should likely get in two laps of the circuit during our hour-long workout this week, despite the slower surface that gravel provides in-game. Our workout this week is "Happy Hour," a bit less aggressive of a ride than the last few iterations of this ride. As always, if the workout is a bit too spicy for your taste, just turn ERG mode off or dial the bias down in the Companion App -- so long as you remain pedaling, you'll stay with the group.

      Tuesday Structured Training
      The original Slowtwitch Indoors event, and still one that you can frequently find Slowman himself on, Tuesday Structured Training will be heading to Richmond this week for The Fan Flats. It's a circuit that's easy to overlook if you're trying to earn your route badges in game, so we figured we'd get some strong laps in and check that off your completion checklist. This workout is another Dan classic: In Place of Motorpace.

      Rise and Grind
      My personal favorite ride of the week, the East Coast early morning sufferfest heads to an old favorite of the ride leading team: the Innsbruckring. This ride will feature yours truly, hanging out in the C group this week with the red sweeper beacon. Alex, Mark, and Shaun are your respective ride leaders across the B, C, and D categories. As always, we'll run a neutral first lap before contesting the sprint on each subsequent lap. We'll run category maxes on the legsnapper climb shortly before the run into the sprint: 4.0 for Bs, 3.2 for Cs, and 2.5 for Ds. Work together and have fun!

      Hilly Vanilli
      You'll find Slowtwitch contributors across all of our groups this week, so no matter if you're a B, C, or D cyclist, you'll find someone from Slowtwitch there! This week we head to London for the Keith Hill After Party. This event-only loop should keep respective groups together until the final climb. Work together, everyone!

      Casual Fridays
      Our newest ride, partnered with our friends at Every Man Jack, it's all designed to have a good chat with a few efforts sprinkled in to prep you for a long weekend of training or racing. Sean Hale is both your fearless ride leader and author of this week's workout. We'll be lapping the Watopia Flat course -- another route that's easy to overlook on your quest to check off all the route badges in Zwift.

      Slowtwitch Indoors Runs


      Speaking of places that it's easy to find Slowtwitchers on Zwift, the most popular place in all of Watopia to run is May Field. That's where you can find us every Monday and Wednesday, 7 AM and 7 PM Eastern, putting out some miles for 30 to 35 minutes at a time. Pace is as conversational or speedy as you'd like to make it, and we'll be there to provide some positive vibes as you're getting your miles in. You'll never know who might make a guest appearance on the track at the same time.

      We'll see you on Zwift!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_The_Need_for_Speed_7527.html b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_The_Need_for_Speed_7527.html new file mode 100644 index 0000000..1525917 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_The_Need_for_Speed_7527.html @@ -0,0 +1,648 @@ + + + + +This Week on Slowtwitch Indoors: The Need for Speed - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Week on Slowtwitch Indoors: The Need for Speed

      + +
      +
      +
      +

      As indoor training season heats up, so does the pace on most of our Zwift rides and runs. We've been seeing lots of new avatars and new records set across our event calendar. It's an exciting time to be on Slowtwitch Indoors!

      +

      For those joining us for the first time, particularly on either Rise & Grind or Hilly Vanilli, we do want to advise you of some best practices:

      Choosing the Right Category
      The amount of fun you will have on these rides is distinctly tied to whether or not you registered for the right pace group. We offer B, C, and D categories. The primary goal of these rides is to keep most of us in the group, with sweepers working in conjunction with leaders to bring everyone back together after particularly nasty climbs or sprints. We do use the fence as necessary to prevent flyers heading off the front and wrecking the ride -- so if you want to stay in our Slowtwitch kits and ride together, please be mindful of your group registration.

      Signaling to Our Sweepers
      We use the Zwift Companion app to message one another in-game. Given the size of our rides, it can be difficult for a ride leader or sweeper to notice that there's somebody falling off the back. If you're starting to see the elastic snap to the group, use the Companion app to message that you need a wheel back up to the group. That's our signal to send someone back to you (and to have the front of the ride ease up a little bit).

      +

      Now with that out of the way, let's get down to our actual events for this week:

      Slowtwitch 100/100 Challenge Runs


      The 100/100 Challenge enters its second full week, again with 4 support runs for you to check the box off with. We have slightly extended the length of these events in order to make sure that we hit the 30 minute minimum (in the case that you're not hitting the 3 mile minimum during the time limit).

      Monday, 7 AM Eastern
      Monday, 7 PM Eastern
      Wednesday, 7 AM Eastern
      Wednesday, 7 PM Eastern

      The goal of these runs, as always, is to just be able to make it to the next one. Although if you're really feeling frisky, you can always try to take on the mile time that Anthony Famiglietti just ran on Zwift. (Some of us are discussing on our Reader Forum this 3:55 mile on a treadmill.)

      +

      Slowtwitch Indoors Rides


      Ride of the Valkyries
      Our women's only ride will tackle a Watopia classic this week, heading to the Volcano and alternating trips around the Circuit and up the climb in the aptly named Whole Lotta Lava. This week's workout is dubbed "Go Go Go" and features plenty of efforts to get your morning off to a strong start.

      +

      Slowtwitch Structured Training
      We promise that our workout, titled "This Will Only Hurt a Little," will in fact only hurt a little. By Wednesday. Maybe Thursday. This one has a little bit of everything mixed into it, which you'll get to enjoy the scenery of London's Greater Flat loop.

      +

      Rise & Grind
      Get that coffee flowing as we're going for an After Party! The Keith Hill After Party course will get your Thursday moving. We've also shuffled our Ride Leaders this week. Please note: no D leader this week.

      Hilly Vanilli
      Last but not least, our chase ride is now fenced. It's going to be a ride that will require plenty of team work: the Richmond UCI course is not kind to strung out individual groups. We should see at least two plus loops of the course completed during the event.

      We'll see you on Zwift!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_To_the_Top__7616.html b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_To_the_Top__7616.html new file mode 100644 index 0000000..ccbd07f --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Week_on_Slowtwitch_Indoors_To_the_Top__7616.html @@ -0,0 +1,645 @@ + + + + +This Week on Slowtwitch Indoors: To the Top! - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Week on Slowtwitch Indoors: To the Top!

      + +
      +
      +
      +

      As we continue to socially distance ourselves and reckon with the certainty that the first half of the outdoor racing season is disappearing before our eyes, we're continuing to ramp things up here for Slowtwitch Indoors. This week, we resume our Monday runs (with one already in the books by the time this publishes), and our rides feature some of the biggest climbs that Zwift has to offer.

      +

      Slowtwitch Indoors Runs


      We continue to base all of our runs out of May Field. This ensures that we all stick somewhat together, no matter the pace, and that we can engage with one another (and most of the other folks who use Zwift run). We now offer you 4 opportunities to run: 7 AM or 7 PM Eastern, Monday and Wednesday. If you're looking for an easy way to get on Zwift run, the RUNN Wireless Sensor is looking like your best bet. We'll slowly but surely add some more structure to these -- but for now, boot up the Companion app and chat with your fellow runners to get your social connection fix.

      +

      Slowtwitch Indoors Rides


      Ride of the Valkyries
      Our women's only training ride gets the most mixed terrain of the week. The structured group training ride heads to Sand and Sequioas for 60 minutes of fun. We've knocked the intensity down slightly from some of the past weeks, this time going with "Over Ones" for the workout. Just keep the pedals moving and you'll stay banded with the group.

      Tuesday Structured Training
      Our original ride goes back to its original name; consider the change my New Coke moment. At any rate, we go to Yorkshire for the Royal Pump Room 8 course. We usually save that gem for the structured training events as, well, without the rubber-band it's darn near impossible to keep everybody together. Keep pedaling, make sure your FTP is set correctly, and get to it.

      Rise & Grind
      Our Thursday morning ride is heading for Zwift's largest mountain. Road to Sky is our adventure for this week. Be prepared to stick around for some bonus time if you're looking to reach the top. Ride leaders will likely be in the upper end of their w/KG range, and any sweeping will just be a second group on the road.

      Hilly Vanilli Chase
      Our last ride of the week heads to a lesser-known course in Watopia: the Magnificent 8. This course is more rolling than anything else, featuring a reverse trip up the hilly KOM. As always, groups depart 5 minutes apart from one another. Work together, utilize the draft, and have plenty of fun together.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/This_Week_s_SlowZwift_Events_7253.html b/site/slowtwitch.com/www/Indoor_Training/This_Week_s_SlowZwift_Events_7253.html new file mode 100644 index 0000000..723b1f0 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/This_Week_s_SlowZwift_Events_7253.html @@ -0,0 +1,644 @@ + + + + +This Week's SlowZwift Events - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      This Week's SlowZwift Events

      + +
      +
      +
      +

      Ride of the Valkyries, women’s only ride, Kelly Burns Gallagher leads this week. I tweaked last week’s Tuesday Structured Training workout just for this week’s Valkyries. I call it "Rainbow Grotto," because it’s so pretty when you look at the workout profile from inside the Game.

      I just finished my Tuesday Structured training workout for this week, which is called, "The Sea, the Sea, the Sea." It will become apparent why, if you ride it with me Tuesday.

      Could someone win a literary award for the Zwift workouts he or she writes? I don’t know why not. If rap or rock lyrics qualify, why not insightful prose inside of a structured training workout? I’ve begun to write mine with this in mind, and I promise that if you ride these workouts with me, I'll take you through all sorts of emotions.

      Here are the profiles of this week’s structured training events…

      +

      Last week, I didn’t think I could finish the whole 70 minutes of the Hilly Vanilli. One of our group took off early on a solo ride, and stayed away the whole time. The C group caught us (the D group) at 55 minutes. Great ride.

      I got a call from Ian Murray this afternoon. He just rode his first three Zwift rides. He doesn’t know why it took him this long to start Zwifting. (Nobody ever listens to me.) Here are our Zwift events this week, and each lasts just around an hour:

      Tuesday, 6am Eastern: Ride of the Valkyries
      Tuesday, 4pm Pacific: Tuesday Structured Training
      Thursday, 5:30am Eastern: Slowtwitch Rise & Grind
      Thursday, 3:55pm (D), 4:00pm (C) and 4:05pm (B) Pacific: Hilly Vanilli

      What I do, I go right away, click the link, enter the event, done. Then I don't have to remember if I entered. So, right there. Above. Click... the... link. Enter. Hit back button. Click the next link. Rinse and repeat.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Tips_and_Tricks_for_IRONMAN_s_VR_Championship_Series_7689.html b/site/slowtwitch.com/www/Indoor_Training/Tips_and_Tricks_for_IRONMAN_s_VR_Championship_Series_7689.html new file mode 100644 index 0000000..f3b6666 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Tips_and_Tricks_for_IRONMAN_s_VR_Championship_Series_7689.html @@ -0,0 +1,644 @@ + + + + +Tips and Tricks for IRONMAN's VR Championship Series - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Tips and Tricks for IRONMAN's VR Championship Series

      + +
      +
      +
      +

      I recently finished the second of IRONMAN’s VR Championship Series events. I highly encourage everyone to join the VR races. They are free, but more importantly, they are a big part of what we are here for. We train, and we race. We enjoy the challenges of the work, the details of the preparation, and the satisfaction of the execution. The VR races helps exercise those muscles in a new way.

      One thing that makes the Championship Series races a little more appealing and challenging, at least to me, is the structure. The Classic Division has the same open rules as the first nine VR races—you can do the events in any order, over the entire weekend, inside or outside. The Championship Series rules make it a little more like a real race.

      With that in mind: pay attention to the rules! It is not as easy as you would think. Although IRONMAN publishes the rules with each race, there are a few that may need a little more of your attention.

      Overall Tips and Tricks


      Practice with your technology. Do not use new technology for the first time in an event. Before your race, get everything setup and do some runs and bikes with Rouvy to ensure that everything syncs to the VR platform. The first time I used Rouvy, it crashed. I am so glad it was not the race.

      +

      Finish in 12 hours. I almost failed this up in my 2nd race because I mixed up my math. I finished my 10K run with 3 minutes to spare: from 8:28 PM to 8:26 AM.

      Do the events in order. This seems obvious, but it is different from the original races.

      Wear a heart rate monitor for the whole event.Heart rate is required for the run. It is not explicitly stated for the Rouvy bike. Better be safe than sorry.

      Key Run Rules


      Run outside. Treadmill runs do not count.

      Wear a GPS device. Run results must come from a GPS device with heart rate data. It must be uploaded to the VR platform through one of their approved workout tracking sites and publicly available.

      Do not accidentally start. IRONMAN will take the first eligible run distance you do. So, if you run a light 5K to loosen up, it will take that as your first run.

      Get the distances right. The rules state that the distances are based on kilometers, and they will not round up. For example, 1.8 miles is short of the 3K distance; you must run at least 1.86 miles.

      Do not run more than necessary. IRONMAN will take the total time of the distance that meets the criteria. So, if you run 4 km for the 3 km event, it will apply the total 4 km time; it does not clip your time down to what it would have been for the required minimum distance.

      Do not lose elevation. If you are not running on a track, plan your run with an uphill buffer at the end. You do not want to be finishing an out-and-back on a downhill and be a few hundredths short.

      Key Bike Rules


      Ride inside on Rouvy. Pretty self-explanatory.

      Use an approved trainer. This got a lot of attention after the first race. Originally, there were only 17 approved trainer models. The Tacx Neo 2 was among the notable omissions. After the race, anyone on a Neo 2 was disqualified. So, too, were users of Saris’ Hammer. After a few Facebook threads and direct contacts from several Slowtwitchers, IRONMAN reviewed the trainer list with Rouvy and significantly expanded the list of eligible trainers. They retroactively reinstated the results of those disqualified and awarded points.

      Race the official Rouvy course. IRONMAN releases and announces the course about an hour before the event start time. You cannot just meet the distance; you must ride on the official course for the event.

      Make sure Rouvy is in Race mode. When you use Rouvy for the first time, it defaults to “Training,” so you will need to change this. This is as simple as clicking the “Race” button on the main screen.

      Racing in the Championship Series exposed me to Rouvy, and it helped normalize the playing field as much as possible. People in cooler climates or flatter regions have a definite advantage over me. I love the virtual Rouvy world, and I will be giving Zwift a try next because of this. And for me, the results matter. All the qualifying races for 70.3 Worlds near me are cancelled, and I want to get a Taupo slot. But even if I do not get a slot, I had a lot of fun. This week is the 70.3 distance VR event, and I am looking forward to it.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/To_Erg_Or_Not_To_Erg_7649.html b/site/slowtwitch.com/www/Indoor_Training/To_Erg_Or_Not_To_Erg_7649.html new file mode 100644 index 0000000..62e9c72 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/To_Erg_Or_Not_To_Erg_7649.html @@ -0,0 +1,649 @@ + + + + +To Erg Or Not To Erg - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      To Erg Or Not To Erg

      + +
      +
      +
      +

      [Author's note: I originally wrote this piece internally for Zwift, but it seemed a bit too technical. I think that makes it perfect for Slowtwitch. However, it should be noted that I work for Zwift, and that's why it's so Zwift-centric. But Erg Mode is not at all unique to Zwift. Virtually all major training platforms will support this. As do most good headunits that can control a smart trainer. I just don't use those other platforms. But from a "when/how/should I use ERG mode?" perspective, the Zwift aspect of this is entirely irrelevant. I have my obvious biases, but Erg Mode is by no means a special or unique feature.

      I wrote this about the same time that this thread - "Why you need to quit ERG mode..." - appeared on our forum. I didn't write it in response that thread; it was just a happy coincidence. This is the much longer version of the answer I gave in there.
      ]

      +

      Erg Mode - available when using Zwift in Workout Mode with a compatible "smart" trainer - means that the trainer will adjust the resistance the trainer applies in order to help maintain the prescribed power of a given interval. Erg mode is a reference to the CGS unit of energy - the Erg - which is 10-7 joules or, approximately, the amount of energy used by a common house-fly to do a push-up.

      Humans generate power on a bicycle by applying a force to the pedals. Because the pedal is at the end of the crank - a lever-arm - this force generates a torque which drives the system via the chain and sprocket. There are two ways to generate more power, which is defined as a unit of work within a period of time. You can either apply more force in order to do more work - pedal harder - or you can decrease the unit of time it takes to complete that unit of work - pedal faster. Erg Mode works by adjusting the resistance up as your cadence drops - meaning you need to generate more force - or by decreasing the resistance as your cadence increases - because you are doing your work more quickly.

      How rapidly this change happens varies from trainer to trainer, and is somewhat limited by how often Zwift and the trainer communicate. Zwift also does some work behind the scenes to smooth things out and to keep the changes from being too drastic in one direction or another. For this reason, Erg Mode often is not a great choice for workouts with a lot of very short and very big changes in power. But good smart trainers respond quite quickly, and I find that Erg Mode works well for intervals as short as 20 seconds; anything shorter than that and you probably will want to take care of things yourself by shifting and how hard you pedal.

      +

      Erg Mode is the default option for Workout Mode in Zwift, so if you've never actually paid attention to whether or not Erg Mode was on or not, you may have been using it this whole time without ever knowing. For most typical workouts - things like SST or The MCarthy Special, Erg Mode works just fine. As with (almost) every approach to training, there are those who insist that it has no place in "proper" training and those who insist it's the only way to train indoors. Again, as with most every approach to training, the truth is somewhere in between.

      The "opposite" of Erg Mode in Zwift is Incline Mode; Incline Mode allows you to manually set the resistance of the trainer to be as hard - or as easy - as you wish. When you're just riding around in Zwift not doing a workout, you are in Sim - or Simulation - Mode, where Zwift is telling the trainer what the gradient is of the road that you're on (or, more precisely, the gradient of the road you will be on by the time the trainer gets the message Zwift is sending). If you turn Erg Mode to Off using Zwift Companion or the blue Control Strip, you'll see another set of Up/Down arrow buttons appear; these allow you to increase or decrease the resistance of the trainer (shown as an orange, vertical analog scale to the right of the HUD). When Erg Mode is On, the trainer will change its resistance automatically based on the target power of the given block. (If you're in a Free Ride block, you will almost always be placed into Incline mode unless your coach or the workout author has set it up to preserve the terrain of Zwift.) Neither Erg Mode nor Incline Mode is either "better" or "worse." And to the larger point, doing formal, structured workouts is also not better or worse training than riding around or racing. The key is using the tools at your disposal to help you achieve the training stimulus you are seeking.

      Erg Mode is simply another tool to put in your training toolkit. For a lot of people, Erg Mode is just simpler, because it does a lot of the thinking for you. If you like to just tune out - especially during very long blocks - Erg Mode is great. But just thinking of it as a way to make it easier to think less is to shortchange all the ways that it can impact your training. I really started thinking more about how - and when - to use Erg Mode when I switched from being a triathlete - where the training is pretty monotonous, generally long, and fairly steady. Long distance triathlon cycling is, as Mark Allen liked to say, "an exercise in who can whisper the loudest." It's supposed to be very steady. That sort of training probably doesn't matter too much when it comes to Erg On or Erg Off; I'd actually say that there's a slight preference to having Erg Off because it's really important to develop the skill - and it is a skill - of pacing yourself evenly and steadily across a long period of time and - especially - across variable terrain. Cadence is also not particularly important for triathletes and, if anything, there's a preference for lower cadences where neuromuscular coordination is rarely - if ever - a limiter.

      But when I started training on the track, cadence became a much more important element of performance. On the track, with a single, fixed gear, cadence and power and speed are all inextricably linked. And it was because of this that I really started to delve deep into how best to use Erg Mode.

      +

      For workouts where cadence is the key parameter, use Erg Mode. If you have to focus on both the power and cadence, it's hard to do either well. If hitting a specific cadence is important - and there is compelling research that high-cadence pedaling is a specific skill that you need to train, then using Erg Mode is a great way to make sure you hit that. Interestingly, I'd say it's particularly for high-cadence workouts that Erg Mode is most beneficial; for low-cadence workouts it can go either way. For really low-cadence workouts, Erg Mode may actually be problematic because it can be easy for the resistance to spike so high as to lock the trainer up or if you spike the power even a bit the trainer resistance can drop off making it harder to stay on the cadence. Typically, for really low cadence workouts, I will use Incline Mode and just punch the resistance up to a level where I can find a good rhythm. But at high cadence - especially if the power is also high, I find that Erg Mode can really allow me to hit a cadence and power that I'd struggle to if I had to focus on both.

      Many people think of Erg Mode as a tool to keep your power from dropping, but it's also at least as important as a tool to keep your power from spiking. Erg Mode is just as useful when it comes to keeping you from riding too hard as it is when it comes to keeping you from riding too easy. Especially early in a long ride or early in a high power interval where you might start with a big spike in the power, Erg Mode can be invaluable in terms of keeping a lid on your power. In particular for very high power efforts where it's very difficult to pace them evenly - you'll want to start out too hard at the beginning and want to fade at the end, Erg Mode can help to keep your pacing even. Erg Mode can also be good for keeping those of us with competitive streaks in check when someone rides by us at a time when we're supposed to be riding along at base.

      On the flipside, sometimes it's important to be able to make a big surge and then settle in. Or for very short efforts where the power cannot be sustainable - like a sprint. In these cases, Erg Mode runs counter to the way that our body works, and it should not be used. Sprinting in Erg Mode is extremely counterproductive - the resistance won't be high enough at the start and will be much too high by the time your legs inevitably start to fade.

      One of the major criticisms of Erg Mode is that it prevents you from learning how to pace yourself properly; but Erg Mode can actually be the single best way to teach yourself to pace evenly (which is really what people mean when they say "properly") because it helps you actually feel what it's like to maintain a steady power - especially when coupled with a target cadence. It allows you to experience something that you might struggle to understand at first. In that same way, Erg Mode can also be a great reminder to keep your power in check and to prevent too much power - something that's far more detrimental in long races than too little power - by immediately dropping the resistance if you start to spike the force on the pedals.

      Ramp Blocks in Zwift also present a unique experience when coupled with Erg Mode that can be difficult - if not impossible - to replicate outdoors. By slowly ramping the power up or down, Zwift can help guide you to actually increase the power as your fatigue builds or to help you drop your power in a controlled way over the course of an interval. Controlling this precise step up or step down in power is something that can be virtually impossible to do outside because of terrain and weather. Even in Zwift, without Erg Mode, these precise ramps up - and down - are nearly impossible. This type of stimulus is something that's unique to the Ramp + Erg Mode pairing.

      Especially with the introduction of Workout Mode in Zwift Companion, it's really easy to turn Erg Mode on and off and to experience the difference in how it makes a workout feel. One interesting experiment can be to do two sets of a given block of intervals spaced out with a long rest block between and to see how you feel if you do the first round with Erg Mode on and how you feel if you do the first round with Erg Mode off. But the biggest benefit will really come to thinking more about how Erg Mode can interact with different workout elements and build workouts - and engage and disengage Erg Mode within it - to put a new spin on your training. Don't just set it and forget; make how you use Erg Mode a conscious decision in your approach to structured training. For me, it has made training both more productive and more enjoyable (in that type-2 fun way).

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Trainerroad_One_Year_In_7101.html b/site/slowtwitch.com/www/Indoor_Training/Trainerroad_One_Year_In_7101.html new file mode 100644 index 0000000..d05290f --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Trainerroad_One_Year_In_7101.html @@ -0,0 +1,647 @@ + + + + +Trainerroad, One Year In - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Trainerroad, One Year In

      + +
      +
      +
      +

      Are you a reluctant upgrader? I am. I hate it when my brain feels like it's doing a bench press and can't quite get the bar up.

      But I'm not as bad as Sika Henry! She's Mikey (hates everything) in the Life Cereal commercial (for you who go back that far). I pretty much forced Trainerroad on her last year. "Just try it for 6 weeks," I said. She whined, but she was a good sport.

      +

      Here's what she said after 6 weeks. I thought I'd to back a year later and ask her again. Slowtwitch's longitudinal study on neo-Luddittism.

      SLOWTWITCH: When I first introduced you to Trainerroad, about a year and a half ago, you said, “I guess I’ve been hiding under a rock because I’ve never heard of Trainerroad until you mentioned it… the lazy part of me doesn’t want to familiarize myself with new technology. I’m pretty old school. I didn’t even start paying attention to cadence until a few months ago.”

      You wrote a piece for Slowtwitch after 6 weeks on Trainerroad, through the lens of a newbie. In other words, you fulfilled your editorial obligation! But – and you can give it to me straight – did you continued with Trainerroad in the year since that article?

      +

      SIKA HENRY: Funny how much has changed within a year! I can’t believe I said that. Initially, you are right, I was using it so that I could write an article based on my experience using the app. Now I can’t live without it. One hundred percent of the workouts I did on my indoor trainer this year were done in TrainerRoad. In fact, I just logged into my account. I’ve done over 120 workouts in Trainerroad this year.

      ST: How many of your cycling miles are indoor over the course of the year, and how much of that is Trainerroad?

      +

      SIKA: I would say about 80 percent of my rides are indoors. These are all done using Trainerroad. My coach, Jonathan Caron, sends me the workouts and I create them using Trainerroad’s Workout Creator. The workouts he prescribes are time-based – not in miles. He typically has me do four 60 to 90 minute workouts during the week and then a long ride – 3 ½ hours – on the weekends. The long ride is the only workout I do outside.

      ST: You were not training with power prior to moving over to Trainerroad. You began using Trainerroad without a power meter. You wrote me, back then, “I’ve always wanted to try a power meter but I like the simplicity of Trainerroad. All I need is a speed sensor and indoor trainer. I have both.” When you did begin training with power I believe it was through the use of your first smart trainer, which was a CycleOps Hammer. How did that change things for you?

      +

      SIKA: You know what? I still agree with that statement. I love the simplicity of Trainerroad. I think it is a great introduction to learning how to ride with power without investing in a power meter. When I first started using the app I was a 3-plus hour cyclist in the half -Ironman distance and knew nothing about power. But I had some pretty big goals this year, like getting down to the 2:30s. I also started working with a new coach – Jonny. He really stressed the importance of learning how to ride outside with a power meter. I was reluctant for months.

      ST: Did you get a power meter for your bike?

      SIKA: Yes! I purchased the Garmin Vector 3s in July. IRONMAN 70.3 Atlantic City in September was my first time racing with a power meter.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Trainerroad_The_Birth_of_Structured_Training_7125.html b/site/slowtwitch.com/www/Indoor_Training/Trainerroad_The_Birth_of_Structured_Training_7125.html new file mode 100644 index 0000000..7425af6 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Trainerroad_The_Birth_of_Structured_Training_7125.html @@ -0,0 +1,647 @@ + + + + +Trainerroad: The Birth of Structured Training - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Trainerroad: The Birth of Structured Training

      + +
      +
      +
      +

      It's the end of a year, a good time to reflect. This site's publisher just wrote about the Chuck Wurster, whose legacy endures as a man, and as an innovator. It was memorialized in the following thesis: If you like today's stationary tech, you must thank Chuck Wurster and Computrainer for a great deal of it.

      Computrainer was both a hardware and software platform. Yes, you could build workouts in its companion software Racermate so, arguably, that system was both the first smart trainer and the first structured training option. But if we're to honor Computrainer for the impact it made on today's stationary landscape, Trainerroad deserves the mantle as the birthplace of structured training as we now know it.

      +

      The image above is of Trainerroad at Interbike, in 2012, the year after its intro (aboard is Drew Edsall). The image below is of a makeshift sound booth then used to record Trainerroad instructional videos.

      Simply put, if Zwift's workout builder—and the "Zwift Industry" analogs, like zwofactory.com—are a market reaction, it's to Trainerroad, not to Computrainer or anything else. As of 2016 Slowtwitchers close to half (44 percent) chose Trainerroad as their favorite stationary platform, and the other 11 named platforms (Zwift, Sufferfest, FulGaz, Golden Cheetah, PerfPro and the rest) split up the remainder.

      I think when we look back to 2011, and what we had available to us for indoor training tools at the time, TrainerRoad was a disruptive technology. As of 2011 we had the following:

      - Computrainer, with RacerMate software: expensive, proprietary, tedious to set up, and kind of "hardware first" - the software supported the hardware rather than the hardware supporting the software. At the time, the Computrainer was relatively high on training quality and somewhat high on entertainment, especially when used in a group environment.

      +

      - Maybe Tacx with their trainers with then-proprietary software: At that time, I didn't have much visibility to Tacx, as they were mostly a European entity and not available at my local shops. So, my perception was that they were out of my reach and I probably didn't give them the credit they deserved as being decent competition to Computrainer/Racermate. Tacx was probably right there in training quality and entertainment but, honestly, they were then and, to U.S. audience, still this amorphous European entity.

      - Lots of dumb trainer options: At this point, we were still contemplating if a fluid trainer was really that much better than a magnetic trainer. Training quality depended as much on the owner as the tool itself—you had to want to get in a quality workout and had to educate yourself on how to do that. Entertainment was relatively low.

      - Entertainment products: We had DVDs like Spinervals, RealRides, Sufferfest (still in their infancy), to at least provide a bit of entertainment, and a little bit of structure based on Rate of Perceived Exertion. Combined with a dumb trainer, they could provide a medium quality workout, somewhat specific to the athlete, with a bit more entertainment value.

      - Other factors, such as spin bikes/classes, coaches, power meters, treadmills, and ANT+: These weren't at the heart of indoor training in 2011, but were there as either alternatives or influencers. I would even throw Slowtwitch and other cycling/triathlon forums in there as influencers.

      All of these things by themselves were nice, but disjointed at the time. The net result was that the pain cave was somewhat torturous and on a scale of 1-10 most people probably got a workout quality of 1 or 2, with the more knowledgeable and resourceful folks getting better than that based on their knowledge, having a good coach, or spending the money on the CT or Tacx solutions.

      +

      Trainerroad (TR) disrupted the market by bringing all of these disjointed elements together. (The image above was a whimsical moment showing TR's support stuff at work with your queries.) The glue was the Virtual Power construct. It wasn't their idea (I'm not sure who really invented it, but I found articles dating back to the early 2000's on the concept), but what they did is bring training with power to the masses. They combined some software geekery, the piles of relatively affordable trainers, ANT+, a coach willing to design workouts for them, a bit of workout organization in the form of training plans, and a few basic sensors together into a clean, easy-to-read interface with data that could be shared with a coach and turned the indoor training world on its head.

      I was part of their beta, and actually learned about it from a Slowtwitch thread. I had the pieces and parts—a basic CycleOps magnetic trainer, Garmin speed sensor, cadence sensor, and heart rate monitor, an ANT+ 1.0 dongle and a computer—and now my world was opened to real structured training, using virtual power, which, while not perfect, is still a decent number that can be trained to, versus RPE or heart rate.

      Virtual Power took that training experience from a 1 or 2 up to about 6 or 7 to anybody who could put those few parts together. The training was not only better, but now more people could do it. The bar was raised pretty high with the introduction of TR.

      TR built a solid meat-and-potatoes meal on the following elements:

      1. Virtual Power
      2. Open communications protocol
      3. Wide open hardware compatibility (both trainers and electronic devices: PCs, Macs, iPads, iPhones, Android)
      4. A good selection of structured workouts included with the package
      5. A good selection of training plans tailored to specific disciplines
      6. Decent metric/analytic output
      7. Ease of use
      8. Affordability

      As of now, numbers 1 thru 3 above can be leveraged by anybody now, 7 and 8 are relative to the individual user, but 4 thru 6 are the heart of quality structured training; not only each second of each workout, but how each workout fits into the week, month and season. The folks at TR were smart to do that all off the shelf, as part of the package. There is a lot of value in their workouts and training plans. It's like 1,000 different Spinervals videos for $15 a month, plus whatever people might spend to have a coach write up a generic plan, but better.

      TR also got ahead of the curve with their workout creator.

      TR also introduced the support for video workouts, which meant having a coach translate those RPE Sufferfest or Spinerval videos into power-based workouts. I remember the first time I did the Sufferfest's Fight Club video with TR. My concept of riding a 10 out of 10 during that 16th hidden interval was really about a 6. Once I learned to trust the series of blue boxes and the yellow squiggly line on screen, I was training a lot more intelligently without having to think about it. And I was entertained. (Below is a vintage pic of TR's basement photo studio.)

      +

      With that said, TR started a few trends that brought everything closer: building on the open ANT+ and subsequent ANT-FE-C and Bluetooth tech means they could focus on software rather than hardware, and others could follow suit. I think we're seeing that realized now by the hardware manufacturers, as they learn to be a part of the overall indoor training landscape. Computrainer missed the break on that. Wahoo, Tacx, Kinetic, Elite, and CycleOps leverage ANT-FE-C and Bluetooth to make hardware that can talk to any software, while spinning off their software products so they can work with any hardware. The software options continue to expand and get better, the hardware options continue to get better, and the technologies and standards make for a more plug-and-play world.

      We're now at a point where we're experiencing the results of a distillation process that was sparked by TR. They inspired others to look at their concept (PeriPedal, PerfPRO haven't been mentioned yet) and try to expand on it. The software competition is fierce, with each product putting their spin on what TR did while trying to create their own identity. The hardware has also become kind of a parallel arms race. Late 2016 gave us the Hammer, Magnus, Kinetic Smart Control, Tacx Flux, and others, along with what was already out. Late 2018 has now brought us nice refinements on all of these in the H2, M2, newest generation Wahoo products, Kinetic Control units, Kinetic R1, and updated Neo and Flux, not to mention what Elite has going on. The competition is beneficial for us, as consumers.

      You can probably take any element on my list above about what TR does right and find someone who does any one of those better, but TR did them all sooner, established a standard, while doing it for a low price, and TrainerRoad is a solid, easy-to-use product. It is still is hard to beat.

      The challenge is how to enhance the product without negatively impacting their strengths. They do a really good steak and potato, so do they stick with what they do well or start introducing other stuff to the menu? Zwift is more entertaining than TR, but it wasn't as effective as a training tool until they added structured workouts. TR's workout selection is still top-notch, with well over 1,000 workouts. I am not aware of anybody who has done a better job with the training plans, but Zwift is probably the one that is gaining most rapidly. Rouvy is a lot of fun and offers a different entertainment appeal than Zwift, but really hasn't a library of training plans yet.

      Is TR as relevant today as it was in 2011? Here's a story of 2 pain caves. I spent part of last Sunday setting up a CycleOps H2 with Zwift, Apple TV 4K, and a 75" U4K TV at a customer's ultimate pain cave. He and I had discussed this a few weeks ago, he did some further research, and settled on Zwift because he felt it met his needs. His main objective was to have fun and get structured workouts.

      The day before, Saturday, was a very different story. New customer looking to buy a triathlon bike. She had a budget of $1,200, including pedals, hydration, and other accessories. She admitted she knew nothing about cycling or training, although she has a Kinetic Road Machine trainer and a free coach that gives her workouts based on HR zones. She rides about once a week because she would rather do other stuff. We looked at and discussed Rouvy, Zwift, PerfPRO Studio, Sufferfest, and TrainerRoad. I showed her my low-budget pain cave. I showed her how I like to ride Rouvy because it entertains me, but I'm also not training for anything in particular. We talked about FTP, IF, TSS, periodization, and the TR 8-week training plans. She decided she liked Trainerroad because of the training plans and the simplicity. She liked the idea of seeing the yellow squiggly line and blue boxes. That made sense to her. I shared the recent Slowtwitch article on the TR Calendar, as well as the Rouvy and Zwift articles. She was certain TR is exactly what she needs. Those training plans are the best guide available, short of a coach. Trainerroad checks all the boxes for her, Zwift seemed a bit overwhelming for her, Rouvy doesn't provide sufficient guidance for her, PerfPRO Studio is too hard. For her, it kept coming back to the TR training plans.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Trainerroad_s_Workout_Calendar_7090.html b/site/slowtwitch.com/www/Indoor_Training/Trainerroad_s_Workout_Calendar_7090.html new file mode 100644 index 0000000..16b8be9 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Trainerroad_s_Workout_Calendar_7090.html @@ -0,0 +1,647 @@ + + + + +Trainerroad's Workout Calendar - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Trainerroad's Workout Calendar

      + +
      +
      +
      +

      Trainerroad launched its new Workout Calendar 6 weeks ago. Here’s an overview of that calendar, along with comments. They’re not my comments, rather those from a number of Slowtwichers who’ve used the Trainerroad Calendar. Mind, Slowtwitchers are famous for liking nothing new, and that tracks with human nature: We don’t like change; we don’t like the unfamiliar. That said, there are some detractors, but also a lot of new fans (as you’ll see).

      The Calendar isn’t designed to be a full-service training software. Trainerroad says it “long-felt that current calendar options for cyclists are either too complex, too rigid,” or, "not powerful enough.” The Trainerroad Calendar wasn’t designed to replace Training Peaks, rather to be an alternative for those who don’t want the complexity of a program that powerful. (Perhaps a better comparison would be to Training Peaks’ free version.)

      +

      Or, the Slowtwitch Training Log, and that would be fair and square. What does the Trainerroad Calendar endeavor to do?

      • All workouts, inside or out, auto-populate on your calendar (outdoor rides via sync with Strava and Garmin Connect).

      • Run and swim workouts from TR's training plans, or workouts you schedule yourself also appear in your calendar now.

      • Add TR's training plans to the calendar and indicate which days you usually work out. The plan then auto-populates onto your calendar on the appropriate days for your schedule.

      • Schedule outdoor rides, indoor rides, custom workouts and other activities and see their future implications in your Training Stress Chart.

      • If you have to adjust your plan, drag and drop workouts, or push all of your training out one week, or pull it back one week.

      • Add annotations to provide context when looking at your calendar. This is really helpful for when you get sick, or have travel that interrupts your training, etc.

      Many Slowtwitchers are avid followers of TR's podcast, and here's a bit from its podcast where TR's principals explain the Calendar:

      +

      +

      Okay. All sounds super. How have Slowtwitchers taken to the Calendar?

      One user, flynnzu, wrote, "That TR Calendar was really a swing and miss by TR in my mind and a surprisingly tone deaf response to the current market. 90 percent of their customers who have any interest in that level of tracking and monitoring are already using TP which specializes in just that functionality."

      I question highly this 90 percent metric, and a user responded to this post saying, "Speak for yourself. I love the new Calendar and so do most of my friends. You have to remember triathletes and TP are a very niche market. It's not the primary focus. Primary focus is on the masses - everyday Joe rider - who uses TR."

      Another wrote, "I love it and it'll be perfect once it syncs with google cal. Don't have to subscribe or pay for anything else."

      In fact, flynnzu wrote back later, saying, "Fair point - I was failing to take into consideration the limitations of the free TP functionality. My perspective was actually pretty limited in the way I was thinking about it now that I reconsider - I withdraw my comments!... I am a huge TR fan so I hope this does work out for them. They are still ironing out the Calendar and improving it pretty regularly so I am sure it will continue to get better. Their customer service and interaction is top notch so I am sure the product will continue to evolve based on what the customers want."

      +

      Here's a candid statement from STP on our forum that seems to sum it up for a lot of users: " It seems a lot of folks just want a basic calendar and were either struggling with the free version of TP (which is pretty worthless) or had nothing like a calendar at all. Now can get a decent calendar with just a TR subscription. TR's analytics and calendar suck compared to the paid version of TP, but if you had nothing before, it is a very significant improvement."

      I traded a lot of private messages with TR's Calendar users, and STP's comments are a succinct distillation of what I heard. (Thank you to those who indulged me questions.)

      The paid version of Training Peaks goes well beyond TR's Calendar's mission. But, that's the point of the Calendar: to appeal to those who don't want to go down deep and stay down long in training analytics. The images above are screenshots from a desktop and an iPads; I have Android and iPhone screenshots too, but landscape images show best on our architecture here. Besides the functionality, one must admire Trainerroad's Calendar from a strategic perspective, reminiscent of Wahoo. It's hard not to applaud clever and unpredictable companies, that color outside the lines - our own industry's versions of Apple - and it makes one wonder what we'll see next.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Turbo_Trainers_5556.html b/site/slowtwitch.com/www/Indoor_Training/Turbo_Trainers_5556.html new file mode 100644 index 0000000..7629660 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Turbo_Trainers_5556.html @@ -0,0 +1,640 @@ + + + + +Turbo Trainers - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Turbo Trainers

      + +
      +
      +
      +

      The ubiquitous product for indoor cycling was and remains the turbo trainer. It's a device that a cyclist attaches to the rear of his bike, suspending it just above the ground, exerting some form of resistance while you pedal. (Think mouse and wheel.)

      +

      Such force can be applied either to the rear tire or the cassette, and this is one way to stick trainers into categories. The method for generating resistance is the other: Wind, Magnetic, Fluid.

      +

      + +

      + +

      Wind, magnetic and fluid trainers, also referred to as wheel-on trainers, attach to a bike via a clamp that locks to the rear wheel QR skewer. The clamp is adjustable so that the rear tire can be aligned in the middle of the roller. The roller, which is attached to a flywheel, is then adjusted via a clutch knob so that it presses against the rear tire. The flywheel attached to the roller provides the resistance in the above photo; one can see how fans create the resistance. A flywheel also works to overcome the resistance, in that it provides inertia that keeps the drum from spinning down. Generally the larger the flywheel the more realistic the ride.

      Wind resistant trainers are among the least expensive ($100-and-up) turbo trainers and have the fewest features. The resistance is limited, they are noisier, and have the least realistic feel of the trainers available, the exception the LeMond Revolution trainer. Wind trainers are generally the entry level models.

      +

      + +

      +

      Magnetic trainers are mid-range trainer. They operate the same way, but the flywheel has metal disc/plate inside the unit than spins and generates drag (magnetic field generating resistance). Some newer models use a current to create an electromagnetic field that can be controlled digitally and therefore more accurately. The ability to exert and measure resistance precisely, and to control and record the session remotely, makes it a Smart Trainer and will be examined separately. Overall, magnetic trainers provide improved resistance and feel over wind trainers with a marginal increase in price ($200 and up) and are a cost effective option.

      +

      + +

      +

      Fluid trainers may be the most popular type of trainer on the market and for good reason. They operate by a blade spinning though a fluid which in a self-contained unit. Fluid trainers offer a more realistic feel than wind and magnetic trainers, are quieter, and provide sufficient resistance. They do have a reputation for getting hot but most of the newer models have a fan to prevent overheating. Some users also report that these trainers can leak fluid, but this is rare. Prices on Fluid trainers can start at around $350.

      +

      + +

      +

      Within the fluid trainer category there is one unique model, the Kurt Kinetic Rock and Roll ($549). It sits on a pivot that allows the bike frame to tilt left to right while pedalling to deliver a more realistic experience, and possibly less stress on the frame. It is a nice feature that is really noticeable during brief, hard efforts, or anytime you get out of the saddle.

      +

      + +

      +

      Direct drive trainers are the rage. They are also the most expensive of all the trainers. Unlike all of the wheel-on models that attach via a QR skewer, direct drive trainers literally take the place of the rear wheel. Direct drive trainers are often quieter, more stable, provide more resistance, and a better feel, and do not wear down a tire. Did I say they are the most expensive? One of the more popular direct drive trainers, the Wahoo Kickr, sells for $1199 and this is not the upper limit.

      Still, direct drive trainers are coming down in price. Dumb direct drive trainers using the same sort of resistance options discussed above (wind, mag, fluid) start around $400.

      +

      + +

      +

      Trainers have become an effective and efficient tool in training because it allows for cycling in a controlled environment. Choosing a trainer requires forethought on how one intends to use the trainer. There are advantages and disadvantages to each type of trainer so recommending the ‘best' trainer will always depend on the user and questions such as: Do you have a power meter? Is noise an issue? Do you intend on training occasionally or often on the trainer? And do you want to take advantage of software such as Zwift or TrainerRoad during your training rides?

      If you are interested in how to use leverage various software programs to take advantage of smart trainers, stay tuned for an article dedicated to smart trainers and virtual training.

      +

      Need more help? Check out some of the links to similar articles just below this one.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Turbo_Trainers_and_Rollers_6677.html b/site/slowtwitch.com/www/Indoor_Training/Turbo_Trainers_and_Rollers_6677.html new file mode 100644 index 0000000..6e7a486 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Turbo_Trainers_and_Rollers_6677.html @@ -0,0 +1,618 @@ + + + + +Turbo Trainers and Rollers - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Turbo Trainers and Rollers

      + +
      +
      +
      +

      There’s no room for dispute if you’re riding aboard a trainer: If you aren’t training smart, you’re training dumb. But there are times when dumb is a virtue! (Why pay for smart, if dumb is all you need?)

      Third party applications (like Zwift and Trainerroad) control the resistance on smart trainers. Traditional turbo trainers (aka dumb trainers) don’t work like that. You, the rider, need to make the bike harder or easier to pedal if you want to match the effort requirements of these third party software products. Usually (but not always!) that’s done just by changing the gears on your bike.

      Some dumb trainers also have additional features such as Ant+ or BTLE transmissions and manual resistance control, but this category really focusses on the types of resistance used: wind, magnets, fluid, rollers.

      Types of Turbo Trainers

      +

      Within the turbo trainer category, there are different types of turbo trainers, each with advantages. The trade-offs are price, sound, maximum wattages, and feel.

      Now, before we go further, remember, there are a couple of ways to make your dumb trainer compatible with these new, cool, smart and relatively inexpensive software platforms. One is for your bike to be equipped with methods of measuring the requisite metrics (power, speed, cadence). Lacking these, there is a very inexpensive method described in our first primer on Zwift. An Ant+ dongle, a Wahoo Speed and Cadence sensor, and you’re up and running for under $100 (not counting the cost of your dumb trainer). I’ll finish this up with a little more on the specifics of this (Virtual Power), but let’s just get right into trainers.

      Wind/Air

      +

      An air, or wind, resistance turbo trainer relies on an external fan within a flywheel that utilizes air to provide the resistance. The harder one pedals, the greater the resistance, and the greater the noise! These trainers are deficient in road feel but do allow riders to hit higher wattages than some other dumb trainers. Air or wind resistance trainers are often considered an entry level trainer with costs starting at $100. The CycleOps Wind Trainer ($169) is one of the most popular in this category and is compatible with many of the various virtual training apps.

      Magnetic

      +

      Magnetic turbo trainers are the mid-level dumb trainer and work in a similar fashion to air resistance trainers however the flywheel relies on magnets within the unit to create a magnetic field of resistance as opposed to air resistance. The resistance provided by magnetic trainers is linear when compared to speed, that is the resistance gets progressively harder.

      Smart Trainers that rely on this type of resistance use an electric current to create an electromagnetic field of resistance. Unlike smart trainers, the resistance level for magnetic trainers often is limited to the range of 700-1000 Watts and must be controlled manually via a lever that can attach to the handlebars.

      Magnetic trainers provide improved resistance, are quieter, and offer better road feel versus wind trainers, with only a slight increase in price ($200+), making them a popular choice as long as the max wattage is not a deciding factor.

      This category has been relatively stagnant in terms of development until recently with the release of the STAC Zero (below).

      +

      This trainer relies on magnets attached to the rear wheel that interact with the trainer to create an eddy current (magnetic resistance) which allows for greater resistance than traditional turbo trainers. STAC claims that riders can generate up to 2000 watts under certain conditions. Also, because there is no flywheel and the bike’s tire doesn’t touch the trainer, the only sound heard is from the bike’s drivetrain, hence the name STAC Zero for zero sound. There are two models of the STAC Zero – Basic and Power meter – with the pricing starting at $439 Canadian. Slowtwitch plans to do some hands-on testing in the near future with the Stac Zero.

      Fluid

      +

      Fluid trainers may be the most popular trainer type on the market. These have a more realistic feel than wind and magnetic trainers, they are quieter, and allow riders to produce more power. Like air and magnetic resistance trainers they operate via a flywheel but use fluid within the self-contained flywheel as the means of creating resistance. The resistance is more gradual and lifelike.

      Early models tended to suffer from fluid leaks but this is now the exception. They are prone to generating a great deal of heat, which can make the flywheel a bit of a hazard if you have young children. With pricing starting around $350, fluid trainers are a popular choice among indoor riders not using smart trainers. The CycleOps Fluid2 ($350+) is one of the more popular trainers in this this category.

      +

      Within the fluid trainers, there is one model that is unique, the Kurt Kinetic Rock and Roll ($449 to $549). This model sits on a pivot that allows the bike frame to tilt left to right while pedaling to create a bit more realism, and possibly less stress on the frame. It is a nice feature that is really noticeable during brief, hard efforts, or anytime you get out of the saddle.

      Also, the Kurt Kinetic range of trainers allow for the addition of a smart control power unit which is their response to the growth of the smart trainer market. The Rock and Roll Smart has its own built-in sensors, which preclude the need to buy the sensors mentioned above and are fully compliant with Zwift, Trainerroad and the other apps you’ll want to use.

      Rollers

      +

      Among triathletes, rollers appear to be the least common type of indoor trainer used but among pure cyclists, rollers are quite popular. Rollers consists of either metal or plastic drums and some rails to keep the rollers in place. Simple design, simple concept, pedal or fall over as the bike is not secured to the rollers as is the case with all the other types of indoor trainers. Rollers have a number of advantages over traditional trainers such as improving balance and core strength, pedaling technique and they best simulate outdoor riding. Rollers also tend to be quiet, inexpensive, and are easily stowed away under a bed or in a closet. Most of the well-known bike trainer companies such as Tacx, CycleOps, Kurt Kinetic produce rollers at a retail cost starting at around $300.

      Slowtwitch has written just about rollers in the past, and there are plenty of threads on our reader forum about rollers including one just this past week. Note one obvious reason rollers are less popular among triathletes than roadies: It’s harder to ride the rollers in the aero position than it is to ride rollers only on a road bike.

      +

      Some brands provide more features, such as smart rollers. One such example is the Omnium Trainer which is a basic roller with a front fork that acts like training-wheels for riders new to rollers. Other brands such as Tacx offer similar options. Here’s a recent discussion of this trainer on our reader forum.

      What is Virtual Power

      As noted above, most training platforms will accept a good guess of the power you generate while riding a dumb trainer or rollers. This opens the door to many of the benefits of virtual training without the resistance control features found in smart trainers.

      +

      Virtual Power is a cost-efficient means of training with power, without owning a power meter or a smart trainer. A trainer’s resistance can be linear (resistance changes proportional to speed), or the most road-like feel being progressive (resistance changes as one changes gears or pedals harder). Based on the trainer’s known power curve, which is basically the power (watts) to maintain a certain speed, and the speed reading from the rear tire or flywheel on a trainer, a power value (virtual power) can be calculated based on this relationship. It is important to note that each turbo trainer has its own specific power curve. Here is an example of the power curves for turbo trainers.

      Dumb Trainers and Virtual Training

      Just because you don’t have a smart trainer does not mean you cannot use some of the virtual training and racing platforms on the market. Some of the virtual training platforms will function using virtual power, minus the resistance control feature.

      +

      For example, Zwift only requires a speed sensor on your rear wheel and a supported trainer which Zwift can use its power curve to establish virtual power (or zPower in Zwift). To use virtual power with Zwift, pair a speed sensor and “classic trainer” and Zwift will calculate a virtual power for the ride. (Described in vivid detail on our intro-to-Zwift series, installment-1.)

      TrainerRoad has a similar process. Under the device screen in TrainerRoad, simply “Enable Virtual Power” and then pair your bike trainer from the drop-down menu and then pair a speed and cadence sensor. TrainerRoad has a nice description on setting up virtual power with its software.

      Each virtual training platform offers tips and compatible trainers on their website for training with virtual power so I recommend following their directions when using their software platforms.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Update_on_the_RUNN_Treadmill_Sensor_7654.html b/site/slowtwitch.com/www/Indoor_Training/Update_on_the_RUNN_Treadmill_Sensor_7654.html new file mode 100644 index 0000000..845bfef --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Update_on_the_RUNN_Treadmill_Sensor_7654.html @@ -0,0 +1,648 @@ + + + + +Update on the RUNN Treadmill Sensor - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Update on the RUNN Treadmill Sensor

      + +
      +
      +
      +

      A fellow asked, in a Facebook post following my review of the RUNN, “Anyone use this on any of the Curved Manual Treadmills?” Today I’m writing for the 8 people worldwide who’re actually interested in this question (because I said I would), and I thought I’d answer a few other questions, such as, how accurate it this device?

      I took the RUNN sensor off my Woodway and placed it on a Trueform Runner, which is a curved, manual (and quite popular) mid-priced treadmill with a segmented belt, like the Woodway. But let’s back up, and I’ll remind folks what the RUNN sensor is and why it's an important device.

      +

      A company called North Pole Engineering produced a sensor that you could attach to your treadmill and it would measure the distance traveled and send a Bluetooth signal to (for example) Zwift. This is a big deal, because a lot of treadmills don’t send BLE signals and you can’t run on a platform like Zwift without that signal sent. This sensor became widely available in January (or before, but I became aware of it in January). The folks on our Reader Forum have been discussing it.

      So I got one, stuck it on my Woodway, and whammo, great, a BLE signal. I wrote about it. Since that time, a couple of things have happened. One is, North Pole Engineering came out with a firmware update. You’ll find it on the App Store and Google Play, and it’s called Configurez.

      +

      Second, I not only tested to see if this device worked on the Trueform, events conspired to cause me to wonder how accurate it is, and how well it comports to Trueform’s own analytics.

      The install was easy. I had my issues with the RUNN on the Woodway, and I mean the physical install. Getting the spacing right. I had to jury-rig it a little bit. I wrote about that in my review of the RUNN.

      For the purposes of what I'm writing here, I picked the RUNN up off the deck of my Woodway, stuck it on the deck of my Trueform, and stuck 3 stickers that RUNN provides on the belt of the Trueform. (North Pole gives you a spit-ton of these stickers; you won’t run out). I booted up Zwift, it saw the RUNN and that was it. The entire process may have taken me 5 minutes.

      Just to make sure there weren’t any dropouts, and that all was honky-dory, I thought I’d make sure that the mileage logged on Zwift (which is the display of the mileage logged on the RUNN) and the mileage traveled on the Trueform treadmill’s own analytics matched. They didn’t. Oops. Which was off?

      +

      King Arthur had Excalibur. Brienne of Tarth had Oathkeeper. I have Truthteller. My measuring wheel. I set all devices to zero and commenced. Here is the result. When Zwift says I’ve traveled exactly one mile Truthteller says I’ve traveled 5,260 feet. This means the RUNN device is pretty darned accurate. Almost certainly more accurate than your treadmill. (Treadmills are notoriously inaccurate.) How close is the RUNN to the wheel? At an 8min/mile pace, the RUNN is less than 2 seconds (1.8 seconds) off of what my measuring wheel says.

      Meanwhile when my Trueform says I’ve gone a mile, Truthteller says I've traveled 5680 feet. That means that when I’m running an 8-mile mile the treadmill thinks I'm running an 8:36 seconds mile. You can see, below, I’m given the option in Zwift to either pair with the Trueform’s BLE signal, or the RUNN sensor. I'm pairing to the RUNN!

      +

      I’ll be writing more about the Trueform mill, because it’s quite a nice mill in a number of ways. Just, if you buy one, and if they give you an option of either spending less money and not having a BLE connection, or paying a premium for a BLE connection, don’t pay the premium! Just pay $99 for a RUNN and you’ll get an accurate measure.

      Finally, I have not updated the firmware on my RUNN yet. I took it off one mill, slapped it on another, and did not calibrate it. This is just how accurate these things are.

      So, that answers the question about the RUNN on at least this manual, curved treadmill. Works perfectly.

      +

      There are other ways to skin this, if you're looking to get a BLE connection into Zwift. One is to just run with a footpod. But there are multiple ways to get a BLE signal from your mill into Zwift.

      Make sure your mill doesn't already emit a BLE signal. If it doesn't, before you get a RUNN realize that North Pole Engineering makes another device called a GEM Retro, and it's a slicker add-on, it's internal, goes inside your mill's console, just, your mill has to have what's called a CSAFE port for the Gem Retro to plug into. It's also $99, like the RUNN, and is the cleaner option. However!

      You see how treadmills can be inaccurate? If you get your own version of Truthteller, you can see if your mill is delivering accuracy or not, because the built-in BLE, or the Gem Retro, will only take what you see on your console and deliver it to Zwift. You may still be best off with a RUNN, if accuracy as well as connectivity matters to you.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Upscaling_Your_Pain_Cave_with_the_Saris_MP1_Nfinity_7474.html b/site/slowtwitch.com/www/Indoor_Training/Upscaling_Your_Pain_Cave_with_the_Saris_MP1_Nfinity_7474.html new file mode 100644 index 0000000..d81f0d7 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Upscaling_Your_Pain_Cave_with_the_Saris_MP1_Nfinity_7474.html @@ -0,0 +1,647 @@ + + + + +Upscaling Your Pain Cave with the Saris MP1 Nfinity - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Upscaling Your Pain Cave with the Saris MP1 Nfinity

      + +
      +
      +
      +

      In the expo area at the Hawaiian Ironman in Kona, Saris had its new H3 smart, direct drive trainer strapped atop a platform on rails. This construction is being marketed as the MP1 Nfinity Trainer Platform. I’ve heard it referred to as a motion platform and a rocker plate. Whatever you want to call it, it was one of the coolest things I experienced at the expo this year.

      +

      So what is this thing? It’s a platform atop which you attach your trainer, and it rocks. I don’t mean “rocks” in the “first and fourth finger-extended, head banging” sense, rather in the “sway to and fro” sense, and you should take that quite literally. The MP1 platform doesn’t just rock & roll from side-to-side, it also moves fore and aft. If you’ve ever had to dodge a wheel coming back toward you as the rider in front of you stood to get out of the saddle, you have a sense of what Saris is trying to emulate here, and even the small amount of time I spent on it in the expo was enough to convince me that they’re onto something. Stand out of the saddle when you’re on this and you can feel the bike shoot back a wee bit, just as it does on the road. Really put some power into the pedals, and the bike moves underneath you, a bit forward, a bit back, a bit sideways. The MP1 animates the bike – the bolt of lightning awakening Frankenstein’s monster – providing a stationary riding experience that finally reacts like a bike on the road. It’s alive!

      It felt “real” enough that I found myself wanting to strap the trainers from the other manufacturers onto it just to (un)level the playing field in the feel contest. Pretty impressive for something that doesn’t look like much when it’s just sitting there.

      Here is a GCN-produced video of the platform. Yes, this is a paid presentation, and is so-labeled in the video. Still, it’s a well-made video and I’m including it here because it’ll give you a sense of how it reacts to the rider’s inputs, and you can see what this thing looks like underneath, if you want to watch the entire video. (There are some other things in the video, but it’s mostly about the MP1.)

      +



      +

      Speaking of other trainers, have you noticed that trainers have taken a next step? The Wahoo Kickr Climb injects ascending into the ride. What would happen if a Kickr was affixed to the back of the MP1, and a Kickr Climb to the front? Would you then get a 100 percent accurate road feel, excluding descents? Meaning, the road feel of the MP1 plus the capacity to ride uphill? In and out of the saddle? Does all of that smart trainer hardware fit onto an MP1?

      What about these new units from Wahoo, Tacx, Stages, adjustable bikes with smart trainers built in? Could one place these bikes fit on the MP1? The Kickr Bike stands a chance, because it’s support profile is roughly analogous to the MP1’s profile (below).

      +

      But I haven’t tried to put the bike on the platform; I don’t know how the two would do with each other; I haven’t spoke to Wahoo or Saris about it. Is the bike too heavy for the trainer? A Kickr Bike weighs 93 pounds, a Kickr 47 pounds, so a Kickr and a bike is around 25 pounds lighter than the Kickr Bike alone. Seems like a trivial difference, considering the MP1 is “Tested to the combined weight (rider, bike and trainer) of 350 lb.” On paper, that should mean that the MP1 plus the Kickr Bike allows the rider plus his kit, shoes, paraphernalia to be no more than about 250 pounds. But my speculation is filled with asterisks!! Until I test this contemplated Pain Cave Construct (Kickr Bike + MP1 Nfinity) myself, or hear from Wahoo and Saris that it's a good idea, I can’t recommend this. File this as no more than a thought experiment.

      That said, continuing this thought, other bikes, like the Tacx NEO Bike, that are supported by a wider footprint in the front, and would I fear not fit aboard the MP1. Circling back to the Kickr + Kickr Climb, that certainly seems to be a construct that would fit aboard the MP1. Again, though, I'd like to hear from both brands on this before I get too far over my skis. Or my handlebars.

      The MP1 sells for $1,200, which is equal to the cost of a top quality trainer. If you spent this much on a trainer last year, and you have a like amount of cash trying to jump out of your pocket as stationary season approaches, I think this might take your pain cave up a notch. I don’t see why any trainer wouldn’t work on it, and this is Saris’s view as well. The MP1 is made in Wisconsin, where Saris is headquartered, and has a lifetime warrantee. You can read about it on Saris’s website.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Virtual_Racing_Today_Ironman_and_Others_7613.html b/site/slowtwitch.com/www/Indoor_Training/Virtual_Racing_Today_Ironman_and_Others_7613.html new file mode 100644 index 0000000..856fce0 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Virtual_Racing_Today_Ironman_and_Others_7613.html @@ -0,0 +1,650 @@ + + + + +Virtual Racing Today: Ironman and Others - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Virtual Racing Today: Ironman and Others

      + +
      +
      +
      +

      We are losing more than we’re winning because of this pandemic, and one thing we lost is our ability to congregate with others. The Good Book says, “For where two or three gather in my name, there am I with them.” My governor, Gavin Newsom, says, “For where two or three gather, a state trooper with an open ticket book is with them.”

      That doesn’t mean we can’t run or ride outside. California’s stay-at-home executive order is quite different here than those in Mediterranean European countries. Californians can run and cycle for recreation or fitness outdoors, as long as you and I ride or run solo. That said, most folks are doing their riding indoors. Among Slowtwitchers, 97 percent are still training and 90 percent still cycling. Just, only 27 percent of Slowtwitchers are doing their cycling outdoors. The rest of us are on trainers. This is causing race organizers, timing companies, and others to eye virtual events.

      +

      A consequence of this is the rush toward stationary products and platforms that allow you to cycle, row, swim or run in place. Let’s talk about the stationary landscape, and the virtual landscape, and these are not synonymous. I’ll devote most of what I write to Ironman today, but Ironman is only one part of the virtual racing landscape. I’ll write about others in future installments, as the virtual racing scene (in bike, run, and tri) develops.

      Slowtwitch hosts 4 virtual group runs each week; and 4 virtual cycling events, which are a combo of structured training events and group rides. These all take place on the Zwift platform. We intend to repurpose some of these events into occasional races: perhaps 1 or 2 footraces a month, and 1 or 2 cycling races a month. We’ll keep you updated on our intentions here.

      We’ll host a curated thread on virtual racing on our Reader Forum, and in that thread’s first post we’ll place a wiki calendar that we maintain. We’re talking to a lot of stakeholders now, to find out what they want to do as regards the virtual race landscape. Below is what cycling in Zwift looks like.

      +

      +

      Forms of Virtual Eventing


      In broad strokes, there are two kinds of virtual event “timing companies” out there right now. The first is the stationary platform. Zwift, let us say. Platforms like this – which include Virtugo, RGT and others – are “multiplayer” platforms. What is “multiplayer?” You and I can ride or race side-by-side; all our avatars are on the screen together; they all represent real riders or runners; and our avatars don’t need to honor social distance. Multiplayer is where it’s at; it’s always been where it’s at; it is just easier to appreciate it now, in the age of COVID, because it represents the only opportunity for us all to congregate in groups.

      What do I mean by saying Zwift is a “timing company.” When we race, we need someone to keep track of our times. When we Zwift, Zwift keeps track of our times. We then simply need a way to aggregate and sort the times, that is, to sort them into results, so, we need a “results” company too.

      Garmin and Wahoo are also “timing” companies. Let’s take a STRAVA Challenge as an example. Who times this event? Your Garmin 1030 or your Wahoo ELEMNT ROAM. If it’s a run, your Garmin 945 times it or, failing that, your Stryd Footpod. STRAVA takes the times and delivers “results.”

      So, virtual racing can take place either indoor or outdoor. A STRAVA Challenge is an outdoor virtual event. A Zwift race is a stationary virtual event.

      IRONMAN VR


      Ironman just announced what will be its suite of virtual events: IRONMAN VC (Virtual Club); IRONMMAN VR (Virtual Racing); IRONMAN VR Pro Challenge. There’s a platform. Not yet linked to. Not yet public. Scant details. The only thing we do know is that Ironman is going to hold events (races) on weekends – like they always do – but it appears you can achieve efforts on your own at any time during the weekend. The results of some of these weekend races will generate slots for November’s 70.3 World Championships. There will be both an age-group and a pro component. The pros will race head-to-head, and the “IRONMAN VR Pro Challenge” will debut on April 4th.

      Another publication wrote that, “Ironman Virtual Club will be via Ironman’s own proprietary app and web-based platform, which they have been developing for some time but accelerated given the latest news. (It will not simply piggyback on existing virtual training platforms, but will compete with them.)”

      I don’t want to read more into this than the author intends. Just, Ironman produced a video (below) that accompanies its announcement with Ironman’s pros all running on treadmills and cycling in stationary trainers. The picture this painted in my brain is of Ironman launching a stationary platform, i.e., something like Zwift or Rouvy. I could be wrong, but I doubt this is what Ironman has in mind by a “virtual training platform.”

      +

      +

      In this video Ironman’s CEO Andrew Messick said that, “athletes will submit the data from their connected devices into the platform,” as he seems to be pointing to a smart watch on his wrist. This suggests, to me, that there might be some stationary multiplayer racing by a subset of athletes (i.e., pros), but age-group athletes could submit results from GPS devices. If so, then racing a virtual 70.3 might be an indoor virtual event, as well as an outdoor virtual event, though it is my guess that for the pros it is more likely to be an indoor-only virtual event.

      Can I take a wild guess? We’ve written a fair bit about the Czech stationary cycling platform called Rouvy. This has always been the sort of platform uniquely well suited for a certain Ironman-specific task. On Rouvy, one could build the exact Nice 70.3 World Championship bike course. Further, you could host a virtual race on that course, because Rouvy is a unique portmanteau of graphic avatars – cyclists – overlaid onto a real video. Let’s say that Nice bike course is available to all entrants in a World Championship event like the one held in Nice. The contestants who take the opportunity to pre-ride the course on Rouvy would be better prepared for what awaits them.

      The head-to-head competition among Ironman’s pros will be broadcast on Facebook Watch. I could imagine Ironman partnering with Rouvy for the cycling portion at least. Then to the treadmill for the run. Perhaps a camera filming a pair of athletes, that view inset into a Rouvy screen using a third-party app. The IRONMAN VR Pro Challenge will debut on April 4 and we’ll know if I’ve talked myself into a far-afield fiction. Below is what riding in Rouvy looks like:

      +

      +

      As for you and me, I would imagine there’s a number of ways we could accomplish our rides and runs, either on a treadmill or on the road, based on the tech we have at hand (smart trainers, treadmills, or GPS devices). How would we perform a virtual swim? Beats me. Yes, there’s VASA, and maybe that’s a component of the IRONMAN VR Pro Challenge. (Spoiler prediction: Lionel is the king of the Pain Cave; he’s going to own the IRONMAN VR Pro Challenge.)

      I absolutely could imagine Ironman creating a data aggregation and sorting tool that is unique to them, and they already have a first-class “race result” display and have had for more than a decade.

      Accountability


      Are you watching I Love Lucy reruns in a Barcalounger while your “treadmill” runs a 5k in 14:30? There are 3 different ways I could do that on mine: the Runn app installed on it; the factory Bluetooth signal my treadmill emits; and the hack I installed into the mill’s CSAFE port that also emits a BLE signal. But I don’t worry much about this, because if my treadmill runs a 14:30 5k at some point this pandemic will be over, and when it becomes apparent that nothing in my results before or after the pandemic indicate an ability to run that 5k, I’ll have been found out, and there will be consequences for me to pay.

      The closest method by which we stop a cheat is to mandate a footpod, preferably a Stryd, when running. But it limits participation in a race if everyone has to have that device. There will be a lot of whinging about cheating as we move toward a virtual racing paradigm. I will pay little attention to it. May I close as I opened, with another piece of sage advice from the Good Book? “Sufficient unto the day is the evil thereof,” which retranslated is: Don’t you have better things to worry about?

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Virtual_and_Actual_Bike_Choices_for_Stationary_Racing_7638.html b/site/slowtwitch.com/www/Indoor_Training/Virtual_and_Actual_Bike_Choices_for_Stationary_Racing_7638.html new file mode 100644 index 0000000..bac2fe2 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Virtual_and_Actual_Bike_Choices_for_Stationary_Racing_7638.html @@ -0,0 +1,649 @@ + + + + +Virtual and Actual Bike Choices for Stationary Racing - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Virtual and Actual Bike Choices for Stationary Racing

      + +
      +
      +
      +

      What kind of bike should you ride when you Zwift? Two answers to that question and I'll tackle both: What kind of actual bike you hike your leg over; and what kind of virtual bike you choose to ride “in-game.”

      Both have implications if you race using Zwift as your platform. You might partake in a Zwift race. Or you might choose Zwift as the platform for the cycling leg of a virtual race such as what IRONMAN hosts via its Virtual Club portal.

      +

      Your Actual Bike Choice


      When we poll we consistently see it’s always 95 or 96 percent of you who ride on a trainer. You don’t have to race. You might ride for pleasure or for structured training. Regardless of the reason, it’s stationary! You’re going zero miles an hour. Your wind resistance is… zero. The way to ride a stationary race to win is to ride without concern for aerodynamics, even if the avatar on the screen representing you is moving along a road at 25mph.

      If you’re riding in a Zwift race – assuming it’s a typical mass-start, draft-legal race – you’re at a disadvantage if you’re on a tri bike. You’re best off on a road bike (physically aboard a road race bike, sitting on your trainer). On the same weekend as the inaugural IRONMAN VR race, there was a ridiculously entertaining performance by a bunch of Pro Tour caliber cyclists and… Lionel Sanders, who shocked the announcers and a top-notch pro cycling field by winning. I asked Lionel, “I believe you rode a Canyon road bike. Aeroad? Why?”

      +

      “I would spend absolutely zero time in the TT position on a Zwift race,” he answered, “so there is no need to use the TT bike. The road bike is way better for climbing and sprinting and big power jumps, and if you rode a TT bike you would be at a serious disadvantage relative to everyone on a road bike.”

      This is Zwift racing we’re talking about, which is different than IRONMAN VR racing. The need to accelerate in a hurry, ride out of the saddle without hitting your knees on the backs of the armrests, makes this bike choice easy. “I did the CVR World Cup back in 2018 on a TT bike,” Lionel wrote me. “I finished second and truly believe the road bike could have been the difference. But I didn't have a road bike then.”

      +

      Which take us to the longer, less-hyper ride you might choose for an IRONMAN VR segment. These screenshots here are not of Zwift, but of Rouvy, the cycling platform the pros race in for these livestreamed Virtual Racing events. For those of you who’re racing this weekend's VR race the cycling route is 40k long. You can ride this indoor or outdoor. Your “timing chip” is a Garmin 1030, a Wahoo ELEMNT ROAM, or a 40k ride executed in Rouvy, or in Zwift, or some other stationary platform. If you’re going to ride this 40k outside then yes, choose the tri bike. But if it’s inside, then the bike you place on the trainer depends on what position you’re most efficient in, when riding hard on the trainer for an hour.

      Your position on your tri bike


      What we saw last the weekend on the Facebook Watch livestream of the inaugural IRONMAN VR race was almost every pro riding his or her tri bike on the trainer. Sitting up, most of the time. Hands on the pursuits.

      It may well have been a more or less whimsical effort for some of these folks. If you look at Joe Gambles (below) does he look like he’s taking this seriously? And to be fair to Joe, when he wasn’t riding-while-cradling-bambino he probably spent more time in aero than most of the pros last weekend.

      +

      But I ask you, is there a finish line? Because if the answer is yes, it’s a race! So I asked the folks on our Reader Forum about this and I got a lot of answers, but none that convinced me.

      You might answer that they may as well sit up in a stationary ride, because aerodynamics are not involved. True. But every time you’re sitting up on a tri bike, you’re in an inferior position to what you’d have available to you on a road bike and I don't mean just aerodynamically, but in power production as well. Better if you just had your road bike on that trainer if you're going to sit up.

      +

      There is one very valid reason for these riders to sit up rather than to ride aero: Because the monitor they’re looking at is not positioned properly. When I’m riding a tri bike – above I’m on a Cervelo P3X mounted to a Saris H3 which is sitting on a Saris MP1 platform – my screen is almost dead on the ground, angled up perpendicular to my line of sight. If you wonder why that TV is on the ground, try booting up Zwift on a tri bike, riding in the aero position, with that screen waist high. See how you like it.

      If you’re watching the “game” from, say, a tablet, there’s more freedom of placement. If you’re watching from a larger screen, you must sit the TV near the ground. In the other image inset above that’s where TV sits when I’m Zwifting in front of my road bike, a foot-and-a-half higher.

      Of the 95 percent of you who are riding stationary, just over half of you put your road bikes on the trainer most of the time. Of you tri bike riders almost half are sitting up, just over half are laying down. If you’re laying down you’ll be a lot more comfortable with the TV down on the ground, or with your laptop or tablet fixed in a spot where your eyes are looking down.

      +

      What I heard from some of our Reader Forum folk is that it’s possible some of the pros don’t have road bikes. I believe (and hope) that is unlikely in most cases, and for 2-time Kona Champ Patrick Lange’s the right bike was leaning against the wall.

      Your In-Game Bike Choice


      You don't have to choose the same bike in-game as you ride on the trainer.

      In Zwift you get the effect of the draft. In fact, many or most formal events deploy a feature called “doubledraft” and it’s just what it sounds like. If you choose a tri bike out of the Zwift Garage – if this is your bike choice in-game – then you’ll go a little bit faster when you'r riding alone, but you don’t get the advantage of the draft. When someone joins one of our Slowtwitch weekly Zwift rides and has chosen a tri bike for the in-game bike, we know he’s in for a rough ride, and the ride leader suggests choosing a road bike from the Zwift Garage next time, regardless of whether he’s actually riding a road or tri bike on his trainer.

      If you’re more efficient on a road bike than on a tri bike while on your trainer, you may want to just keep your road bike right there, even if you choose a tri bike as your in-game bike. I asked, and IRONMAN doesn’t care what bike you’re actually aboard for its races, or that your in-game bike type matches your actual bike. If you do choose to execute your IRONMAN VR cycling leg on the trainer, and in Zwift, here are some things you should know.

      First, IRONMAN doesn’t at present care if you are in the company of others in-game when you ride your 40k. Therefore, the very fastest way you would ride your 40k is in a group event – a ride or a race – as long as you don’t get dropped. You might think this is cheating, but it isn’t. What is cheating is placing 120lb in your Zwift profile if you really weigh 160lb. But IRONMAN has no rules against riding your 40k in among a halibut-shaped pack in Zwift. It’s not about your capacity to avoid getting caught, and it’s not that there is no virtual policing for IRONMAN’S virtual races. It’s not a rule violation. There's no violation to catch. At least at this point.

      But if you’re going to do this then you must choose a road bike from the Zwift Garage, or you can’t ride in the group and get a draft.

      If you feel this violates the spirit of the competition, I may well agree with you. So you choose to ride this virtual ride virtuously, and your 40k is ridden solo, in Zwift. You should choose a tri bike from the Zwift Garage. But you may still choose to ride your road bike on the trainer. Clear?

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Wahoo_Acquires_The_Sufferfest_7341.html b/site/slowtwitch.com/www/Indoor_Training/Wahoo_Acquires_The_Sufferfest_7341.html new file mode 100644 index 0000000..9264808 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Wahoo_Acquires_The_Sufferfest_7341.html @@ -0,0 +1,645 @@ + + + + +Wahoo Acquires The Sufferfest - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Wahoo Acquires The Sufferfest

      + +
      +
      +
      +

      What separated Wahoo Fitness from a number of its stationary trainer competitors was the lack of a in-house training platform. Wahoo Fitness, while a robust software company, wasn’t on par with Tacx, BKOOL, Kinetic, Saris (CycleOps), which all had training platforms. With the acquisition of The Sufferfest the Wahoo Fitness ecosystem now has its own structured training platform.

      On the plus side, a user can buy a Wahoo Kickr and this popular brand now has the capacity to immediately market a structured training option. The Sufferfest app provides subscribers access to a comprehensive library of structured cycling and running workouts, and training plans, designed by Neal Henderson and other coaches of his caliber. Both The Sufferfest and Wahoo Fitness each have customers who will immediately become candidates for the sister brand's corresponding good or service.

      +

      On the flip side, Wahoo Fitness has always been the industry’s Switzerland precisely because it didn’t sell a training platform that competed with TrainerRoad, Zwift, Golden Cheetah and the rest. The release included a quote from Wahoo founder Chip Hawkins, who said, “Wahoo remains committed to the growth of the indoor training and fitness sectors and will continue to integrate and collaborate with as many leading software providers as possible.” Wahoo has always been among the most adept at forging great relationships with training and racing platforms, and this acquisition will require Wahoo to pay special attention to those relationships.

      +

      Wahoo said today that, “There are no planned changes in The Sufferfest business offering or structure as a result of this acquisition,” but that is the boilerplate statement for all acquisitions. The only constant is change. Multiplayer is where the action is these, so (for one example) were Tour of Sufferlandria morph into multiplayer (I have no evidence that it will) I for one would not consider change to be unwelcome.

      David McQuillen, founder of The Sufferfest, said of the acquisition of his brand that the new arrangement will mean, "fantastic new concepts and training solutions for our customers." We shall await!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Wahoo_Fitness_Releases_5th_Gen_KICKR_7735.html b/site/slowtwitch.com/www/Indoor_Training/Wahoo_Fitness_Releases_5th_Gen_KICKR_7735.html new file mode 100644 index 0000000..5ff7f0d --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Wahoo_Fitness_Releases_5th_Gen_KICKR_7735.html @@ -0,0 +1,651 @@ + + + + +Wahoo Fitness Releases 5th Gen KICKR - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Wahoo Fitness Releases 5th Gen KICKR

      + +
      +
      +
      +

      When smart direct drive trainers came along they were loud or not quite so; rickety or not quite so; they overheated, lost their Bluetooth signals, froze or lost their resistance altogether. When they did work as hoped there wasn’t a lot of feature choice. They didn’t incline, steer, they weren’t built into adjustable complete bikes. But when paired with compelling online cycling experiences like Zwift they were a marvel, so we all overlooked the kinks and glitches.

      There were bedrock imperatives that attached to this class of trainer, which included the capacity to produce a resistance of, or log an achievement approaching, 2000 or so watts (for those very few who could produce that). There was the need to accommodate the grades offered up by popular apps, and we’ve all ridden at least 16 percent grades on Zwift and certainly more than that in Rouvy.

      +

      Trainers need to be quiet and this new 2020 KICKR I’m writing about today is. The KICKR threw off its reputation as a less quiet trainer (it became apartment-building-acceptable) with its 2018 edition. About nomenclature: I see v5 attached to this in a number of references, but it's the 5th generation of this smart trainer. Point of fact, it's simply called the KICKR.

      This is the year of stationary, for good reasons and bad. I believe I’ve seen as many as 26,000 people riding on Zwift at one point in time, and this speaks to the power of both Zwift, and of a potent, transmittable virus that kept us indoors for an entire spring. Depending on where you’re reading from, you were either exhorted, or legally obliged, to remain indoors and for that reason alone this is the year of stationary.

      But it’s also the year where features and options for smart trainers exploded. If you read these pages regularly you know how high I am on stationary bikes, particularly Wahoo’s KICKR Bike and Tacx’s NEO Bike. (I’ve yet to ride the Stages bike, so I can’t speak to that product.) Tacx gave us one kind of road feel (cobbles, pavers, gravel). Wahoo gave us another with the KICKR Climb and the incline feature in the KICKR Bike.

      +

      Saris gave us another kind of road feel with its MP1 platform, and the feature upgrade that most appeals to me from this new KICKR launch is that sort of road feel. Above is the new KICKR against the backdrop of an MP1 platform, and I had these side-by-side in my workshop so that I could compare the two. The MP1 costs as much as an entire KICKR, which is just a tick under $1,200. (This means the 5th gen KICKR I’m writing about now, just announced today, did not go up in price.)

      But the point if this new KICKR is it grants you the side-to-side road feel that mimics riding on the road. Does it render the MP1 platform moot? Yes. Mind, the MP1 moves underneath you in 3 planes: back/forth, side to side, and rotationally. The KICKR relies only on elastomeric pods called AXIS Feet (below).

      +

      I was skeptical that just these feet could deliver the promise. But the darned things work. The Saris platform is a great concept and exceptionally built, but the movement is not subtle. This is especially the case when out of the saddle. The MP1 requires me to pay very specific attention to balance, and I actually get a little sore from the use of stabilizer muscles after a ride on it. Not so the new KICKR. The ride quality of this KICKR when seated is hard to recognize and you don’t really notice the movement until you get onto a traditional, rigid trainer and then you can tell the difference. But I can get used to just about anything.

      The real value – what makes me “stand up” and notice (pardon the pun) – is when a system delivers roadlike quality when I’m out of the saddle. This is the major value of the AXIS Feet: roadlike quality, but with stability.

      I am not symmetrical on my bike. I favor one side, not only in my pedaling, but in my posture. When I hop onto a trainer this is disconcerting, and it’s helpful if I can adjust the trainer to normalize for the quirky way I ride a bike. The AXIS Feet are robust! They’re big, they’re thick, and they adjust out far enough to allow me to normalize per my idiosyncratic riding style.

      +

      “Of course!” you might say. But the KICKR Bike – which I love – did not give me the same footpod adjustability that the new KICKR does. See that center stabilizing leg that emanates from the enter of the KICKR in the image above? The KICKR Bike has this as well, but it’s too “proud” to allow me to normalize the bike using the adjustable feet. And, no, I can’t replace footpods in the KICKR Bike with the AXIS Feet. But…!

      The AXIS Feet can be retrofit into existing KICKRs of all generations. The pitch of the threads on the feet of KICKRs changed after gen-2, but if you buy AXIS Feet as an accessory, they will come with feet for both thread pitches, so all KICKRs are covered. AXIS Feet will sell for $79.99, so you could enjoy my major upgrade feature of this new KICKR with this purchase, assuming you have a perfectly good KICKR already.

      Here’s a video from Wahoo Fitness that demos the action of the AXIS Feet:

      +

      +

      While I don’t know this, I would wager that AXIS Feet – with their thick elastomeric mass between the KICKR and the floor – improves the already-quiet gen-5 KICKR for those on the floor beneath the rider, and would quiet down the vibration of older-gen KICKRs ridden on flexible floors.

      The new KICKR also claims an increase in accuracy from +/- 2% to +/-1%, and it auto-calibrates (no need for a spindown to calibrate). I haven’t had the KICKR long enough to engage in multiple rides with all my various PMs, but I confess I don’t know if I have the capacity in my workshop (or in my brain) to tweeze out that degree of granularity.

      +

      Let’s get back to the features question because, per the way I began this conversation, we have features now that we didn’t have when smart direct drive trainers burst onto the scene. My understanding of all these new features is incomplete, but it seems to me they are not all currently available in one integrated setup. At any price. Point being, think about what you want before you just start spending money.

      For example, let’s say you’re a dedicated Zwifter. You might want to add steering as a feature. This is available using a STERZO Smart from Elite. But you can only do this if you have a front wheel on the bike you’re riding, and this disqualifies all smart bikes, and the KICKR Climb, unless there’s something I misunderstand. I know of no tech that gets around this. Therefore, you need to choose between incline and steering. You can’t have them both. What would I rather have? I don’t know. Incline most likely. But I haven’t ridden with a STERZO Smart, so I don’t know.

      A note on steering + incline: If you read my review of the KICKR Bike you'll note there are buttons on the shifters that are for future functionality. The idea behind those buttons is, I believe, steering. But that functionality is not yet turned on. Once this goes live, then you'd have incline + steering in the same bike. Not AXIS Feet! But incline + steering. When this moves from thought balloon to functionality I'll let you know.

      +

      This new KICKR was easy to assemble out of the box, and typically easy to configure. You use the Wahoo Fitness app (the all-black icon), not the Wahoo ELEMNT app, and you'll almost certainly get a prompt from the app to upgrade the firmware of the KICKR before you proceed. Do this FIRST. Do this before you attempt to pair with Zwift. While the KICKR should BLE pair with Zwift on its own, I found that not to be the case. I first had to process the firmware upgrade. Other than that, this KICKR is easy plug-and-play, typical of Wahoo products.

      This new Wahoo Fitness KICKR is available now, though (via my best intel) the AXIS Feet as a retrofit for your existing KICKR isn’t schedule to arrive for sale for several more weeks.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Wahoo_Kickr_Bike_On_the_Leading_Edge_of_a_Hot_New_Category_7409.html b/site/slowtwitch.com/www/Indoor_Training/Wahoo_Kickr_Bike_On_the_Leading_Edge_of_a_Hot_New_Category_7409.html new file mode 100644 index 0000000..31037dd --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Wahoo_Kickr_Bike_On_the_Leading_Edge_of_a_Hot_New_Category_7409.html @@ -0,0 +1,650 @@ + + + + +Wahoo Kickr Bike: On the Leading Edge of a Hot New Category - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Wahoo Kickr Bike: On the Leading Edge of a Hot New Category

      + +
      +
      +
      +

      The Kickr Bike might be the new leader (we’ll see) in a relatively new product category: The smart trainer as a complete bike. Don't jump out of your seats quite yet! Yes, I know this isn’t new, any more than the gravel or eBike was new within the past decade. But this category has finally caught fire among serious riders so at a minimum this category is experiencing a reset.

      The first one of these I can remember, and I mean the first truly functional one, was the Velotron, made by Computrainer. It’s still made, now by Quarq. The WattBike has a cult following.

      +

      But bikes with this advanced functionality and precision were often purchased by academia, universities, or propellerhead cyclists. Peloton has broken into the home gym market with a highly functional bike, but I hope that Tacx, Stages and Wahoo didn’t simply try to jump into the market Peloton created. The Kickr Bike was a logical progression, and should’ve been made even if there never was a Peloton bike.

      All these companies variously hit or miss the mark with their bikes, based on what I consider the mark, and my list of functions may include imperatives you don’t find imperative. I’m going to write about the Wahoo Kickr Bike because I think it comes the closest to satisfying my hopes for your use. It still misses some big functionality, but I’m happy it’s come as close as it has.

      Let me say in advance that if I was Wahoo, reading this overview, I'd say, "Dan is writing a great review of a bike we didn't make." And they'd be right. But I have ambitions for bikes in this category, and I know those ambitions will seep into what I write below.

      I haven’t ridden the Wahoo Kickr Bike. Ray over at DC Rainmaker has, so, I defer to him on everything associated with the actual ride experience. The Wahoo Kickr Bike, if you squint real hard, is vaguely similar to the GURU fit bike. It inclines, just like the GURU bike.

      +

      The GURU bike is a fit bike. It’s not a pain cave bike. But the need to emulate a ride up a hill is important for each use case, pain cave or fit simulator. It’s impressive that Wahoo has made a bike that inclines. One reason why a Kickr Bike will be a seller is its ability to point the rider uphill. It’s the bike for the right time because platforms are there on which to use those functions.

      Imagine if all the apps were there, but the handheld phone wasn’t yet invented. Someone would build the phone to host the apps. That’s the Kickr Bike. It exists, now, to use the apps, and the apps are Zwift (primarily), The Sufferfest (which Wahoo Fitness now owns), TrainerRoad, CVRCade, Tour de Giro, FulGaz, Golden Cheetah, and on and on.

      +

      But why is the bike needed at all? Just put your bike on a smart, direct drive trainer, and off you go, for a third or a fourth the cost of this new Wahoo bike. And that’s what most people will do: just buy smart trainers. Why the fuss?

      Use as a stationary bike


      If you buy one of these bikes, of course, there’s less wear and tear on your “day” bike, and you don’t have to take the bike on and off the trainer. So there’s that. Second, as well as I can tell, it’s not really a third the cost if you value incline as part of the experience. When the road on a Zwift ride points up, the bike points up. Kickr Climb is a $600 spend, so a Kickr + Kickr Climb = $1,800. So the Kickr Bike isn't exactly triple the cost of a smart trainer (with incline), but it's still, at $3,500, double the cost.

      Use as a bike prescriber/Position coordinate identifier


      I was talking with a Wahoo executive a couple of months ago. I told him that if he really wanted to do something breakout – with the disclaimer that I’m sure Wahoo wouldn’t be interested in this – Wahoo would make a full bike, like a Peloton, but it would double as a fit bike. It must read out the proper position coordinates, chief among them HX and HY, which are the rise and run from the BB to the handlebar clamp (where the handlebar passes through the stem).

      This isn’t what the Kickr Bike does, however, it does tell you how to set the Kickr Bike up based on the coordinates output by GURU, Trek Precision Fit, and Retül. This is great. I would only add that before Retül, Precision Fit or GURU there was the system that paved the way for all them all. In fact, here’s what you’ll read on the GURU system’s opening page:

      +

      So, with respect to my friends at Wahoo Fitness, perhaps a little respect back? How about a little love for the common ancestor of all current dynamic fit systems with which you interface? Where X/Y geometry and positioning originated? And I’ll just leave that there.

      Back to my discussion with the Wahoo executive: “The bike – which I’m sure you don’t want to make – should not adjust like typical home gym bikes, but should adjust in an X/Y axis – everything up and down, back and forth, horizontal and vertical.” He listened to at all and said, “You may be interested in a forthcoming launch.” The launch I'm writing about now.

      Use as a position tinkering tool


      I’ve long felt, and long said, that the perfect fit bike, for shop use, for bike fitters, is also a great pain cave bike, because it allows consumers to tinker with their positions. The Kickr Bike is great for this, because as explained above it normalizes for your position coordinates, and if you follow our bike fit help threads on our Reader Forum, this is what our thread curators ask for: typically Pad Y and X, or Pad Stack and Reach (same thing). But the Kickr Bike doesn’t give you this, you might reply. It gives you something that basically corresponds to HX/HY. That's good. Of course, it should have just given you HX/HY! See those numbers etched on the tubes of the Kickr Bike? It would've been very easy to simply scribe the numbers bike fitters use. That's one imperative I have for a bike like this, and Wahoo could've done this. But I digress...

      +

      I suspect we'll generate a system that translates your Kickr Bike coordinates to a bike prescribing language our fit experts can use to tell you how to set up your outdoor bike to match your Kickr Bike metrics (or to prescribe a new bike from them).

      Once we do so, and when you read the numbers off your Kickr Bike to Ian Murray, who curates the Canyon Fit Assistance thread on our forum, he’ll tell you exactly what Canyon Speedmax to order. If you give it to Eric Reid on the Cervelo fit assistance thread, he’ll tell you the precise size and configuration of the new Cervelo P-Series we wrote about a few hours ago.

      If we had a bike like this 30 years ago I’d have had a much easier time designing the first tri bikes built for aero bars, and you and I would’ve both had a lot easier time figuring out how to use aerobars most effectively.

      Use as a commercial fit bike


      The Wahoo Fitness Kickr Bike is close, or could've been close. It adjusts with quick releases. You flip the quick release, raise and lower the saddle, or stem, move the handlebars or saddle back and forth, presto, you’re done. But this is the tech in the first fit bikes I designed back around the turn of the century. Bikes like those made by Exit Cycling adjusted in this precise way. Below is a quick release adjusting the Kickr Bike and a detail from an Exit Cycling bike made almost 20 years ago using a similar process for adjustments.

      +

      What evolved is the use of handwheels or even motors to make position adjustments, at least in commercial fit bikes. Why? Who cares? Because a proper fit process is one that grants the fitter the ability make an adjustment while the cyclist is pedaling. Shrinking the time between two adjacent positions to zero is the gold standard.

      The adjustable crank on the Kickr Bike is a riff off the simplest and best adjustable crank ever made, by a clever fellow named Jeff Keller of Sunrise Cyclery. You can see the crank on the Kickr Bike, and inset is Jeff’s crank from probably 20 years ago.

      +

      To be clear, Wahoo wasn’t making a fit bike when it made the Kickr Bike. But they’ve come darned close anyway. It's possible the only motor they could put in a bike like this was the one that inclined the bike, because GURU has patents on motors in fit bikes. GURU may still want to raise an objection, but it’s an interesting question. Countering that objection, it’s not a fit bike!

      Second, Wahoo is simply incorporating the Kickr Climb’s functionality into this adjustable bike. Third, when GURU filed for that patent it didn’t contemplate the bike’s ability to incline. At some later point Dorel Corporation (owners of Cannondale) bought the IP to GURU’s fit system, and I recommended they include incline. (I build incline into every stationary bike I own; in fact here's a video of a recent incline table I built for one of my fit bikes.) GURU's current fit bike has a motor that automatically inclines and declines the bike based on command. This is why the Wahoo Kickr Bike reminds me a lot of that GURU bike. One wonders what kind of pain cave bike GURU, or its sister company Cannondale, could make based on GURU's tech.

      Other bikes, like those made by Exit Cycling (today), Retul, and Purely Custom, Shimano, all adjust via handwheels or some way that allows adjustments to be made while the cyclist is riding. If the bike is made for the purpose, the rider could make those adjustments easily himself while riding.

      No, I don’t think for home use, for position tinkering, it makes much difference. I suspect handwheel adjustments would've added $1000 to the price of the bike, and I doubt that was feasible. Point is, this isn’t quite the bike that fitters can use for bike fitting but, it's perfectly good for pain cave use.

      All that established, once I did sketch out a fit bike that simply bolted into that roughly 2" tube that protrudes forward from a Kickr, and that sketch and the Kickr Bike are pretty close.

      I would have placed different metrics on the bike. I would've placed handwheels in the bike and increased the cost, because I'd have wanted the ultimate pain cave and fit bike all in one. But my list of imperatives is not Wahoo's, and Wahoo made a bike for you, not for your bike fitter. And because Wahoo made the right bike, at the right price, with enough functionality, I say to them provisionally, "Well done." The only provision is, I haven't ridden the bike. I haven't adjusted it, to see if it adjusts in all the ways it needs to. Is the seat clamp a nightmare (they so often are on bikes like these). I haven't sniffed and tasted this bike yet, so I don't know. With those caveats, this looks like the right product at the right time.

      But before I get too lathered, there are at least a half-dozen bikes in this red hot category, and I have a lot of investigating to do.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Watts_Up_in_Watopia_Meet_the_Rise_and_Grind_Team_7723.html b/site/slowtwitch.com/www/Indoor_Training/Watts_Up_in_Watopia_Meet_the_Rise_and_Grind_Team_7723.html new file mode 100644 index 0000000..b6838d4 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Watts_Up_in_Watopia_Meet_the_Rise_and_Grind_Team_7723.html @@ -0,0 +1,646 @@ + + + + +Watts Up in Watopia: Meet the Rise and Grind Team - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Watts Up in Watopia: Meet the Rise and Grind Team

      + +
      +
      +
      +

      Although I have been on Zwift for a long period of time, it wasn't until 18 or so months ago that I really fell in love with it. It was around that point that we expanded Slowtwitch Indoors into our Thursday time slots: the free-ride based Rise and Grind and Hilly Vanilli. These rides are unlike any other on the Slowtwitch Indoors calendar: you can and will get dropped (as I can attest); there's a lot of banter between ride leaders and sweepers; and more than anything, there's a big sense of community each week between the leaders and the riders.

      The 5:35 AM Rise and Grind is East Coast biased, although we see a significant amount of European and Japanese riders joining us. On average, you'll have 100-150 of your fellow riders across B, C, and D categories. In addition to yours truly, here's who you can expect to virtually see every Thursday morning.

      +

      Alex Fuller, Atlanta, GA: B or C Group Lead or Sweep
      Alex has three young kids (ages 6, 3, and 1), a very supportive wife, and like everyone else in triathlon, a mildly addictive personality. He’s pursuing his two-year goal of qualifying for Kona. He finished in 9:31 at Ironman Florida 2019, but he needs to drop another 30 minutes to get the KQ (and probably pick a different Saturday race; Alex is an active member his church and avoids Sunday races and alcohol).

      Alex was one of the inaugural ride leaders. The weekly SlowTwitch newsletter stated it was looking for leaders, and despite never joining a group Zwift ride before, he emailed Dan volunteering to be a backup. The primary leader never showed, so Alex has had the yellow beacon from day 1. Over a year later, he’s still making it up as he goes and making many good friends through the process. Fellow SlowTwitchers are the reason he keeps setting his alarm early on Thursday to ride with friends.

      Oh, and of course, he enjoys out-sprinting the other ride leaders. (Editor's Note: he also enjoys making sure I get dropped out of the B group whenever he is leading it.) Alex is our jack of all trades -- leader, sweeper, B group, C group; whatever we need him to do, he does.

      Alex is also known for having issues while stationary on board his bicycle. His injuries include a trip to the ER due to a speed bump, scars from the indoor trainer, and falling over at stop lights due to not being unclipped in time. As long as he's moving, he's safe. His full-time career is working marketing for UPS; no, he doesn't know where your package is. Sorry.

      +

      Mark Ridings, Gainesville, FL: B or C Group Lead
      Mark is a married 46-year-old father of 2 daughters, 10 and 8 years old, and been involved in triathlon, off and on, since 2010. Before that, he was a college wrestler and then college wrestling coach who, upon leaving the profession, proceeded to gain about 50 pounds.

      In 2009, he bought his first road bike and started riding with his brother. He lost some weight, got a little fitness, and decided to enter a triathlon. It was "just" a local sprint, but had a blast and decided to keep doing them. He wasn’t real serious about training then, but did a couple of sprints, an Olympic and then finally Augusta 70.3 in 2012. Then, for some reason, he stopped; his weight ballooned, past what it had been before, up to almost 250 pounds (on a 5’9” frame). In October 2016, his brother emailed and asked if he wanted to do St. Anthony’s Olympic triathlon that spring. He said yes, started training, and hasn't looked back: he's been hooked ever since.

      When restarting triathlon training, Mark quickly found Zwift and that’s been his platform of choice since. When Dan announced that Slowtwitch was going to start having group rides on Zwift, he joined the first ever Rise and Grind ride on November 1, 2018, and has been a regular participant since. About a month after that first ride, Alex asked Mark if he could help lead. Since then being a part of this ride, and helping leading it, is one of his favorite activities of the week.

      +

      Shaun Gallagher, Middletown, CT: D Group Lead
      Shaun is your fearless D group leader, and has consistently held that mantle over the last few months, and often has the largest group on the Rise and Grind Ride. He began with triathlons back in 2007, and like Mark, took a several year break after completing his 5th Ironman race in 2014. Social connection has been the key draw of triathlon for Shaun from the beginning.

      When he started back to training in 2019, Zwift was an opportunity to enjoy the social aspects of training over the course of the winter and became truly beneficial during the pandemic this year. During the week, his preference for training early in the morning gives him an opportunity to prepare for his day as a program manager for application development at a healthcare provider.

      Shaun's long term athletic goal is to return to Ironman events in the coming years.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Watts_Up_in_Watopia_Mountains_Beta_Races_and_More_7718.html b/site/slowtwitch.com/www/Indoor_Training/Watts_Up_in_Watopia_Mountains_Beta_Races_and_More_7718.html new file mode 100644 index 0000000..39e9159 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Watts_Up_in_Watopia_Mountains_Beta_Races_and_More_7718.html @@ -0,0 +1,644 @@ + + + + +Watts Up in Watopia: Mountains, Beta Races, and More - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Watts Up in Watopia: Mountains, Beta Races, and More

      + +
      +
      +
      +

      For those of us in the Northern Hemisphere, the dog days of summer are here. It's a blowtorch sort of day here at Slowtwitch's New England outpost. For many of us, the options are to either get our training done very early in the day, or to take refuge indoors on Zwift with fans and air conditioners blasting.

      Luckily, Zwift is offering up a lot of variety this week in terrain and events. Let's dive into it.

      World Schedule


      This week is a little funky, as we change over months heading into the weekend, and August's guest world schedule will not be announced until just before the start of the month. Regardless, you've got a good mix of courses available to you, in addition to the always available Watopia:

      Monday: Richmond and New York
      Tuesday: Richmond and New York
      Wednesday: London and Yorkshire
      Thursday: London and Yorkshire
      Friday: London and Yorkshire

      I would suspect that we will see a significant amount of France being mixed into the calendar come August. We'll certainly be using the new world for some of our Slowtwitch Indoors events.

      +

      Slowtwitch Indoors Preview


      Speaking of which: it's a good mix of courses and workouts this week for Slowtwitch Indoors. Ride of the Valkyries will be on the Innsbruckring, whereas Tuesday Structured Training will be on London Classique. Both workouts are relatively stiff this week and come courtesy of Mr. Slowman himself.

      Meanwhile, our Thursday rides will be all about the after party. The event only routes of Libby Hill After Party and Keith Hill After Party will be delighting the riders of Rise and Grind and Hilly Vanilli. New to Rise and Grind is a staggered category start: there are one minute gaps between groups in order to help Bs, Cs, and Ds sort themselves into the appropriate places. Be sure to register for the correct category!

      Our Casual Friday ride, in conjunction with Every Man Jack, sticks in Watopia with the Figure 8 course as a change of pace from the flatter scenery. As always, though, this workout is designed to keep everyone together and warm you up for a weekend of long training or racing. And as always, there are our runs over on May Field to keep you occupied.

      Other Events We Noticed


      Norseman Festival of Climbing
      The mountains are calling! Each day this week, tackle another route with some serious vertical along with fellow climbing enthusiasts. Routes get progressively more difficult throughout the week and conclude with a weekend chock full of racing. You can see the full slate of rides and runs at the above link.

      ZHQ Beta Crit City Races
      These are slightly more difficult to track down -- you'll need to go searching through your Companion App to look for them, as they aren't otherwise tagged. There are various time slots available, but these races are often an opportunity to check out the latest features on Zwift; recently, this included the new Boost Mode activation of "saving" and "discharging" watts at key points during the event. At 12 laps, it's often a very brisk paced event as well.

      We'll see you in Watopia!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Watts_Up_in_Watopia_Ride_with_Jeremiah_Bishop_and_New_Roads_7699.html b/site/slowtwitch.com/www/Indoor_Training/Watts_Up_in_Watopia_Ride_with_Jeremiah_Bishop_and_New_Roads_7699.html new file mode 100644 index 0000000..f05cd78 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Watts_Up_in_Watopia_Ride_with_Jeremiah_Bishop_and_New_Roads_7699.html @@ -0,0 +1,645 @@ + + + + +Watts Up in Watopia: Ride with Jeremiah Bishop and New Roads - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Watts Up in Watopia: Ride with Jeremiah Bishop and New Roads

      + +
      +
      +
      +

      We've got a bunch of interesting things to cover in the virtual world this morning as Zwifters will have opportunities to ride with one of Canyon's featured pros and a bunch of new tarmac in the coming weeks.

      +

      Ride with Jeremiah Bishop


      Canyon athlete and pro mountain biker Jeremiah Bishop is our guest ride leader for this week's Tuesday Structured Training ride! Jeremiah's bringing us to the dirt of Watopia's Jungle Circuit. Spend some money in the Drop Shop on a Grail or Inflite to help speed up the group -- although we will keep everyone banded together, every watt helps! This week's workout is a favorite of Jeremiah's -- the aptly named Criss Cross Intervals will keep you working for a bit.

      Be sure to join us this Tuesday night at 7 PM Eastern. And for a full look at the Slowtwitch Indoors ride and run calendar, head here.

      +

      Let's Go to France!


      Thanks to the virtual Tour de France, we're seeing our first new roads added to Zwift in a while. There are actually two separate new worlds coming: France, which can call the new Ven-Top route it's crown jewel, and Paris, which will feature the famed cobbles of the Champs-Elysées.

      Now, you can't hit these roads quite yet. The planned release schedule sees the virtual TdF races taking place, followed by "explore" events of those stages every two hours afterwards. Once the full route is completed, Zwift confirms that these routes will be available for free-riding. You can be sure that we'll be featuring some of these routes during our Slowtwitch Indoors events in July and August.

      We'll see you in Watopia!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Watts_Up_in_Watopia_This_Week_Our_New_Zwift_Preview_Article_7675.html b/site/slowtwitch.com/www/Indoor_Training/Watts_Up_in_Watopia_This_Week_Our_New_Zwift_Preview_Article_7675.html new file mode 100644 index 0000000..68a8233 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Watts_Up_in_Watopia_This_Week_Our_New_Zwift_Preview_Article_7675.html @@ -0,0 +1,645 @@ + + + + +Watts Up in Watopia This Week? Our New Zwift Preview Article - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Watts Up in Watopia This Week? Our New Zwift Preview Article

      + +
      +
      +
      +

      We are taking a little bit of a new approach to our Zwift event previews here, expanding our view well beyond the 9 weekly events that we host and looking at the broader Zwift event context. That’s why we’re also changing the name of this recurring article, which will still post every Sunday or Monday here at Slowtwitch. We’ll separate things out into three categories: Group Workouts and Rides, Races, and Runs.

      +

      Group Workouts and Rides


      Slowtwitch Indoors Rides, various times: As always, you have five opportunities to ride with us on Zwift: Tuesday morning’s Ride of the Valkyries, Tuesday evening’s Structured Training, our Thursday duo of Rise and Grind and Hilly Vanilli Chase, and Casual Fridays featuring our friends at Every Man Jack.

      Our Tuesday rides this week will be hosted by Kelly Burns Gallagher and Slowman himself, Dan Empfield. These structured training, no-drop rides just force you to keep the pedals turning over in order to stay with the group. They are brisk workouts -- think high TSS, short recovery, and plenty of fun. Thursday rides are more like your local shop ride; stick with the pack with every ounce of energy you have, because getting dropped often means a soft pedal until you get swept up in the group behind. Friday is, as you might expect from the title, a bit more casual in nature, with some efforts to help wake you up for a weekend of training or racing.

      Zwift Tour For All Stage 5, May 20-23: If you’ve been participating in the Tour For All (or you want to kick it off, as Zwift is offering make up dates for all of the other stages soon!), Stage 5 is dubbed “Community Favorites.” Some of the largest teams on Zwift have selected their favorite routes. You can take your pick of Keith Hill After Party, Watopia Pretzel, Sand and Sequoias (women’s only), or Surrey Hills to complete (or start) your tour.

      Pete Stetina Gravelleurs Ride, Tuesday 10:05 AM Eastern: Wanting to test out the Canyon Grail, even virtually? Here’s your opportunity while also riding alongside with Pete Stetina. This C group ride heads to the Jungle Circuit for a few laps. This ride is always a good time with good numbers, so be sure to head on over!

      Team Charles-Barclay Social Ride, Tuesday 12:30 PM Eastern: Join Lucy and Reece for a ride that should have a little bit of everything. Their ride this week will take place on the Innsbruck KOM After Party route. The ride is scheduled to be social until the base of the climb, at which point Lucy and Reece will attempt to chase down the QOM/KOM jersey. We might recommend swapping your wheels out for the ENVE 3.4s, known to be one of the faster sets of climbing wheels.

      CeramicSpeed Group MTB Ride, Wednesday 6:00 AM Eastern: Zwift keeps trying to push the mountain biking segments, and they’re really getting it moving along with their current CeramicSpeed challenge. To unlock the kit in game, as well as to potentially win a CeramicSpeed OSPW system in real life, you have miles to ride on three types of bikes: 25 miles on the MTB, 50 miles on the TT, and 75 miles on the road bike. Join this ride to thunder through nearly all of those MTB miles in one fell swoop.

      +

      Racing on Zwift


      USA Cycling Race Series, Wednesday 5:45 AM/1:55 PM/8:45 PM Eastern: Looking for a Zwift Racing series that feels way more like racing in real life? USA Cycling is here with a weekly series with three start times (six if you include the women’s only versions), four categories, and no power-ups available. I have been racing these events each week, and have felt them to be the most “fair” Zwift races of them all. If you’re a USA Cycling member, there are unique weekly standings and potential prizes available as well. This week will see 2 to 4 laps of the Innsbruckring; the Legsnapper climb will be sure to be the site of numerous attacks.

      Run Events on Zwift


      Slowtwitch Group Runs, Monday and Wednesday at 7 AM and 7 PM Eastern: Looking to rub elbows with your fellow runners on Zwift? There’s no better opportunity than on May Field with a group. Watopia’s track is where you’ll find a significant number of runners at any one given point in time. We run four times per week -- join us some time.

      Turn It Up Tuesdays, various start times: Join your fellow Zwift runners for a series of structured workouts each Tuesday. Whether you’re a first time runner or someone who’s looking for an edge for your next virtual event, or anything in between, these up-tempo workouts are meant to help give you a boost.

      We’ll see you on Zwift!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/We_Noticed_Kanberra_Sport_7704.html b/site/slowtwitch.com/www/Indoor_Training/We_Noticed_Kanberra_Sport_7704.html new file mode 100644 index 0000000..af38a10 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/We_Noticed_Kanberra_Sport_7704.html @@ -0,0 +1,644 @@ + + + + +We Noticed: Kanberra Sport - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      We Noticed: Kanberra Sport

      + +
      +
      +
      +

      When the COVID-19 pandemic swept across the United States, a strange phenomenon also took place: bikes and stationary trainer sales went up. Zwift subscriptions skyrocketed. IRONMAN introduced the VR series. Clearly, indoors is here to stay -- especially true as we continue to see races cancel and athletes seek out their next challenge.

      However, there’s one thing that we know about indoor training: it’s harder on your gear. You don’t get the same evaporative cooling effect that you do outdoors, and sweat just stays soaked through everything: your clothing, your shoes, your bike, the floormat -- everything. And it’s the kind of stink that tends to stick around, even after a thorough scrubbing or trip through the washing machine.

      Through our coverage of the Team Suunto summit back in March (one of the last events before the COVID-19 pandemic), I was introduced to Kanberra’s line of products. Kanberra has sponsored the Suunto team for the past couple of seasons, and I was curious as to why the team had worked with an unknown commodity in the endurance sports world.



      As it turns out, there’s a very good reason: the stuff works.

      Kanberra has existed for approximately 20 years, primarily operating in the air purification and marine industries. Founder Joel Solly is an avid cyclist and wanted to create a series of products appealing to athletes. Kanberra’s line-up of products are all tea tree oil (TTO) based. Unlike your typical off-the-shelf offerings that are labelled TTO, Kanberra’s are certified by ATTIA, Ltd., which is the Australian government’s certifying body for TTO products. An added bonus: TTO is naturally antimicrobial.

      There are three products that I’ve been using over the last few months with my pivot to indoors:

      +

      Chafe Paint - Chafe Paint is Kanberra’s take on an anti-friction cream. It’s a bit tackier than, say, a chamois cream; I would recommend it more for running than cycling personally, although there are those who use it for everything.

      Laundry Additive - If you’re going to try one thing from Kanberra, I would highly recommend this. Between Kelly going for all of her Zwift route badges and all the racing indoors I’ve done, a couple of spritzes of laundry additive in each load of our wash has made all the difference in the world.

      Kanberra Spray - Despite having decent air flow through our basement and a dehumidifier consistently running, it can get pretty ripe down there given the time we spend riding. Two spritzes of this once we’re done riding helps kill off the stench.

      I’ve really been pleasantly surprised to find this product line -- it’s simple, it’s relatively inexpensive, and it works. It’s all you can really ask for.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Welcome_to_Sufferlandria_7552.html b/site/slowtwitch.com/www/Indoor_Training/Welcome_to_Sufferlandria_7552.html new file mode 100644 index 0000000..bca558a --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Welcome_to_Sufferlandria_7552.html @@ -0,0 +1,652 @@ + + + + +Welcome to Sufferlandria - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Welcome to Sufferlandria

      + +
      +
      +
      +

      Before virtual power brought structured, power-based training to the masses, some of the best indoor training tools we had were cycling videos - download a video to the computer or pop a DVD in the player and sweat it out while following along with the on-screen instructions.

      “Go all out now! 10 out of 10! Move those legs fast!”
      “OK, now spin easy for the next minute before we do it again.”

      Aside from the general weirdness of having our TV tell us to do bad things to ourselves, many of these videos were well done and kept us motivated to train. We had quite a few brands to choose from, including Spinervals, The Sufferfest, and many more. Some of them are still available in that same format, while others have changed their approach. The Sufferfest falls into that latter category.

      +

      A Brief History of Sufferlandria

      For those of you who are not familiar, The Sufferfest started out with a series of videos that delved deep into the darker humor of the “pain cave.” When you pressed Play on one of their videos - with titles such as “A Very Dark Place”, “Downward Spiral”, or “Fight Club” - you put yourself in a world where suffering is good, better known as Sufferlandria. As you rode along with the video, you became a Sufferlandrian - racing alongside pros while being told what to do by Director Sportif Grunter von Agony, and being further encouraged by the Minions, the citizens of Sufferlandria, and dreams of becoming a Knight of Sufferlandria. The videos were intense, entertaining, and had purpose. It was a bit weird. It was a little dark. But it was fun. The Sufferfest built a Pain Cave culture.

      Eventually, somewhere around 2012-2013, some power-based applications such as TrainerRoad and PerfPRO Studio mapped out power profiles for The Sufferfest’s workouts and integrated the videos into their apps. With The Sufferfest providing the content and TrainerRoad/PerfPRO recording metrics, you had a really effective tool set.

      Around 2016, seeing where the industry was going, The Sufferfest decided to stop selling standalone videos and offer an app. For some of us, this was a bit confusing - we had the entertaining content and good workouts provided by The Sufferfest, combined with the virtual power, extensive metrics, and relative reliability of TrainerRoad or PerfPRO Studio. Why mess with a good thing? But, in true Sufferlandrian style, The Sufferfest plowed forward and now we’re going to have a look at the results of their efforts, with a tour of modern day Sufferlandria.

      +

      Can I be a Sufferlandrian?

      As with any indoor training app, we need to start with hardware requirements. The Sufferfest app is currently available to Windows PC, MacOS, and iOS users. There is a 14 day free trial, and after that it's $12.99 USD per month or $99 USD per year.

      PC users will need Windows 8.1 or newer, a 1.3 GHz processor, 2 GB of memory, and enough hard drive space to download any videos you want to have available offline. ANT+ is supported via a USB dongle, and Bluetooth is now supported natively if you have the Windows Creator (version 15063) version of Windows 10 and Bluetooth connectivity built into your PC or laptop.

      For Mac users, you’ll need Yosemite (10.10) or newer and Bluetooth.

      iOS users will need iOS 11 with a device that supports Bluetooth 4.0.

      Like most indoor training apps, you’ll need an Internet connection, but The Sufferfest allows for streaming or downloading the videos, so you don’t have to have a fast connection if you plan ahead. You’ll need Internet connectivity when you log in to work out and again to upload your results after you finish.

      For trainer hardware, The Sufferfest suggests you only need a bike and a trainer - you technically don’t have to have any sensors to do the workouts. But if you want to record metrics, The Sufferfest supports virtual power, so you can get by using only an ANT+ or Bluetooth speed sensor. You can further enhance your experience by adding an ANT+ or Bluetooth cadence sensor, heart rate sensor, and/or a power meter. For maximum suffering, The Sufferfest supports many of the popular smart trainers and bikes on the market today.

      +

      Let’s have a look around, shall we?

      When you first download and open the app, you’ll be greeted with a view similar to the screenshot above. If you explore this home page, you’ll quickly realize that The Sufferfest is much more than a cycling application. Alongside the cycling workouts, you’ll also find yoga, mental conditioning, running and strength-training workouts. If I may inject my opinion here for a moment, this is what sets The Sufferfest apart from the others. As a bike fitter, I work with a lot of athletes at all levels who are looking to improve their overall performance, yet are often held back by their own muscle imbalances. They can often benefit from some sort of cross training. To see The Sufferfest embrace this balanced approach to fitness, or what I like to describe as “building the complete cyclist”, is quite exciting.

      +

      Another interesting twist in the plot is what The Sufferfest calls 4DP, short for Four-Dimensional Power. The Sufferfest’s 4DP concept takes power-based training a step farther than FTP (Functional Threshold Power). FTP gives us a number - the average wattage we can sustain for an hour - and allows us to understand what level we ride at and to train accordingly. But, there are a lot of different disciplines within cycling - what kind of rider are you? Are you the kind of person who can push big watts for a short period of time like a MTBer attacking short hills or a crit racer in the final sprint to the line? Or maybe you’re better suited for a time trial on a flat course? Maybe you have no idea?

      While FTP is a core metric in helping us understand how powerful we are as a rider, it doesn’t necessarily give us a good idea of which aspect of cycling we excel. 4DP is designed to help us understand not only at what level we ride, but what kind of rider we are, and describes that using four dimensions - FTP, MAP, AC, and NM. You can learn more about 4DP here.

      As with traditional FTP tests, you establish your 4DP scores using a baseline test provided by The Sufferfest called “Full Frontal”, which is a 1 hour fitness test similar in concept to a traditional FTP test. Like FTP, those numbers don’t do you any good unless you know what to do with them. For the self-coached athlete, The Sufferfest adds a lot of value because it provides training plans according to these strengths. If you have an event coming up that has some hills, and you want to be a better climber, you can choose accordingly.

      +

      Talking about training plans, The Sufferfest offers a wide variety of training plans targeting different types of cycling disciplines or events. At first glance, it appears like there are about 21 plans, including plans for a metric century, a 200 mile gravel grinder, a full distance triathlon, or even a prep plan for the Tour of Sufferlandria, which is coming up February 15-23, 2020.

      The plans vary from 1 week to 12 weeks in length, and are tailored according to your 4DP score. Additionally, they can be customized based on your experience level and are also modular - you can add mental toughness, yoga, and/or strength training into the plan, as well. If you don’t see anything you like here, customized training plans are available.

      Once you’ve scheduled your plan, you can track all of your activity on your calendar. All of your settings, training plan, history, and workouts are accessible in the upper right corner of the app.

      +

      Let’s go for a quick spin!

      Alright, we’ve touched on some of the highlights, so let’s get in and get that first ride. To ride, simply do the following:

      1. Log in and select “Workouts” in the upper right corner of the screen.

      2. Choose a workout and select the “Play” button near the upper right corner of the screen.

      3. Follow the prompts to pair your sensors.

      4. Allow the Minions to tear your legs off. Enjoy the metrics displayed at the bottom of the screen.

      That’s all it takes to get in a workout. The Sufferfest app’s interface is pretty clean and straightforward, and one of the better ones out there when it comes to being able to get in and ride.

      Of course, The Sufferfest is offering so much more than just a workout and we’ve only scratched the surface. We’ll follow up to discuss some more of The Sufferfest app’s features in an upcoming article, as well as touch on The Sufferfest 4DP and training plans again in some upcoming “Indoor Training 101” articles we have planned.

      Thank you for visiting Sufferlandria!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/When_You_Weren_t_Looking_Rouvy_Revisited_7550.html b/site/slowtwitch.com/www/Indoor_Training/When_You_Weren_t_Looking_Rouvy_Revisited_7550.html new file mode 100644 index 0000000..e966dc0 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/When_You_Weren_t_Looking_Rouvy_Revisited_7550.html @@ -0,0 +1,651 @@ + + + + +When You Weren't Looking: Rouvy Revisited - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      When You Weren't Looking: Rouvy Revisited

      + +
      +
      +
      +

      About a year ago, we spent some time with Rouvy, looking at some of the major features of this compelling indoor training platform. You can revisit those articles here:

      Gettin' Groovy with Rouvy - Part 1: Getting Started
      Goodbye Rouvy Tuesday
      Rouvy! (You'll Like the Movie)

      We even did some informal racing in Rouvy earlier this year, taking advantage of Rouvy’s relatively easy means to allow anybody to set up races or similar events. Rouvy has a little bit of everything - structured training, entertainment, and a variety of means to motivate you to ride, regardless of the type of riding you do. Overall, Rouvy brings a lot to the table for a relatively low monthly subscription rate, and is definitely worth consideration.

      Of course, the folks at Rouvy haven’t been resting on their laurels - as we wrap up the holidays and start to think about next year’s race schedule, this is a good opportunity to take a look at what has happened in the Rouvy camp over the last year or so and get caught up. Let’s dive in.

      +

      Routes, routes, and more routes!

      A really enjoyable aspect of Rouvy is being able to enjoy virtual rides in real places around the world, with thousands of video routes available, and with new routes being added all the time. The route library is now well over 3,000 routes and available to view without having a subscription (click here to view the route catalog).

      To contribute to that growth, Rouvy acquired CycleRecon earlier this year, with the intent to leverage CycleRecon’s cycling video recording and production processes and equipment to provide additional high-quality content. Much of Rouvy’s content is user-submitted amateur video and the Rouvy team will continue to gather additional footage in order to provide riders with more professionally-filmed high definition routes.

      CycleRecon’s influence is already being featured in the growing library of AR (Augmented Reality) routes in Rouvy. The AR routes add a fun visual effect to the routes, rendering you or other riders as game-like avatars, along with signs and banners to enhance your video route experience. When we looked at the AR routes about a year ago, they were still in beta, and there were only about 15 routes available. There are now 49 routes listed under the AR Routes category, but if you dig through the catalog a bit more, you’ll find there are quite a few more than that. By my count, there are currently 96 AR routes available.

      Of those 96 routes, there are 8 different countries represented. With CycleRecon having been based in the UK, it makes sense that a big chunk of these routes are in Great Britain (51, to be exact), with routes from Italy, Switzerland, the United States, Czech Republic, France, Australia, and Spain also available. These AR routes range from .4 to 34.4 miles in length, with average grades from 1 to 10%, so there is a wide level of challenges available for you. All of these routes are uploaded in High Definition (1920x1080), with about a fourth of them uploaded at even higher quality 2K (2560x1440). The Rouvy team is working to add 2 new AR routes every week, so we can expect this list to double by this time next year.

      Finally, the AR routes have also been refined even further in terms of the elevation mapping. Rouvy will emulate road slopes to .1 degree, and in an effort to improve the realism, the Rouvy team have updated the AR routes to follow their real world counterparts even more closely in regards to elevation changes. So, if you ride a hilly road with a lot of elevation change, be prepared to shift because you’ll feel the changes in elevation often and quickly!

      +

      Apple TV and Mac support

      Another big change in the Rouvy world is the introduction of Rouvy for Apple TV and Mac. Previously, Rouvy was available only to Windows PC, Android, and iOS users. Adding new technology platforms is a big task, but the new support for Apple TV and Mac users will help expose Rouvy to a wider audience. Available for under $200, Apple TV is one of the most affordable means to indoor training. For the rider on a tight budget, Rouvy and Apple TV is a great option. Although still in beta, Apple TV and Mac users get just about all of the features found on the other platforms, including the AR routes. The one piece that isn’t supported is ANT+ - you will need to be using Bluetooth equipment if you want to use Rouvy on either of these platforms.

      +

      Challenges with real prizes

      Rouvy offers several nice carrots to motivate you as a rider and keep you knocking down miles and achieving goals. Aside from the fun of exploring our world and getting fit doing it, one other fun thing about riding in Rouvy is their challenges. While this is not a new feature since we last looked at Rouvy, it is an ongoing opportunity to win virtual or real prizes. At any given time, there is usually at least one challenge going on, with real prizes. Rouvy has given away smart trainers and other prizes to Rouvy riders who complete the challenges.

      For example, right now you can join the Holiday Tour #6 Challenge: ride 5 video routes, ranging from just under 6 miles to 34.5 miles and you will earn a badge to add to your Rouvy career. If that’s not enough for you, there are still a few days left to join the Kinetic Challenge: you just have to ride one 30km route to be entered for a chance to win a new Kinetic R1 smart trainer. Pretty cool!

      +

      Let’s ride… with real people!

      Some might find Rouvy compelling because it’s a bit more “real world” than the indoor training games like Zwift, RGT, and others. In a sense, the use of video routes provides a bit more natural bridge between riding outdoors and indoor riding that some of the other apps. The Rouvy Team has embraced that transition of outdoor to indoor riding by incorporating several means to interact with others beyond the virtual interface - y’know, like real, analog people.

      Rouvy continues to work on multiple partnerships, including teams from the local to global level. As a Rouvy user with a Strava account, you can join the Rouvy Strava club to keep on top of the latest events and group rides. Rouvy has teamed up with the 2020 Tour de Suisse, and you will soon be able to ride all of the tour routes virtually. There will be events leading up to and during the 2020 TdS in June, including group rides and races on the course. You can keep track of different online Rouvy events on their event page here.

      Another way of interacting with others is by attending one of the Rouvy Cycling Camps. For 2020, Rouvy is offering two camps in Italy, which you can check out here.

      The Spring Cycling Camp will run from March 15 to March 22 in Tuscany. It’s limited to 20 riders, and you better be ready to rip your legs off - you’ll be training with the Rouvy Specialized Cycling Team. The day’s riding will be organized into three groups - the Race Group will cover 90-150km, the Hobby Group will ride 70-100km per day, with some coffee breaks, and the Adventure group will manage 40-60km a day, enjoying some sightseeing and breaks. You can pick and choose which group you ride with on any given day. Pricing including accommodations is just a hair under 900 Euros, and bikes are available for rent.

      The Summer Cycling Camp runs from July 18 to July 25 in Livigno. Also limited to 20 riders, this one is not for the faint of heart. Like the Spring Cycling Camp, it is broken into 3 groups but with different focuses - Road Group 1 is going to be tackling the Italian Alps for 3-5 hours per day, with the sightseeing limited to looking at your stem, hoping your heart doesn’t explode (OK, so I made that last part up). Group 2 will be putting in 2-3 hours per day, with a bit of sightseeing and coffee stops mixed in. The third group is the MTB group - as the name implies, this group will be enjoying the off-road scenery around Livigno on MTB trails. Regardless of which group you choose to ride with (and you can change it up however you like), there is something for everybody. In the meantime, you can train on many of the routes used during the Camps indoor on Rouvy. To top it off, look for an upcoming Rouvy Challenge where you could win a trip to one of the Rouvy Camps!

      +

      Keep watching this space

      We’ll be working on some races in Rouvy in the near future - probably in the late February/early March timeframe, so keep an eye on the Slowtwitch.com front page and in the Slowtwitch forums for a chance to connect and have some fun to test your legs soon. In the meantime, check out how much work the folks at Rouvy have been up to - Rouvy offers a 14 day free trial, so give it a spin!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/When_You_Weren_t_Looking_What_s_New_in_Zwift_7491.html b/site/slowtwitch.com/www/Indoor_Training/When_You_Weren_t_Looking_What_s_New_in_Zwift_7491.html new file mode 100644 index 0000000..f762345 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/When_You_Weren_t_Looking_What_s_New_in_Zwift_7491.html @@ -0,0 +1,655 @@ + + + + +When You Weren't Looking: What's New in Zwift - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      When You Weren't Looking: What's New in Zwift

      + +
      +
      +
      +

      If you’ve been enjoying the outdoor ride season in the Northern Hemisphere, you may have missed some of the latest updates in Zwift over the last few months. The Zwift team has been busy with new features, bug fixes, and other enhancements while you’ve been away. As the weather cools off, it might be time to dust off the indoor trainer, fire up your PC, Mac, iPad, iPhone, or AppleTV and load the Zwift app. As you wait for the download to complete before you ride again, let’s take a look at some of the newest features that the Zwift team has added since we last wrote about Zwift.

      If you’re new to Zwift, you may want to take a look at a few of our past articles covering how to get started Zwifting:

      1. Welcome to Watopia
      2. Zwift, The Deeper Dive
      3. It's Game On With Zwift
      4. Zwift: Let's Dance!
      5. Zwift: A Potent Tool for Training
      6. On Zwift Every Day is Race Day

      Those were originally written in 2017, and a lot has changed since then. Let’s dig in!

      +

      Brave New World(s)

      The most obvious updates are the additions of new courses/worlds and routes. The team at Zwift has been very busy in this regard, having added several miles of new routes with expansions in Watopia and new worlds hosting routes modeled after major race stages. In Zwift parlance, a route is a predefined road, while a course or world is a collection of routes. Watopia is the biggest of 7 worlds, consisting of a total of 28 routes. All total, there are now 64 official free-ride routes in 7 worlds. Mapping out all of the details of the Zwift worlds is beyond the scope of this article, and we’ll cover this in upcoming articles. For now, we’ll talk about a few of the latest additions here.

      +

      Fuego Flats Watopia Expansion

      Inspired by American deserts, Fuego Flats consists of roughly 9 miles of new, relatively flat roads that enabled the addition of 5 new routes. Fuego Flats includes two new towns - Saddle Springs and Desert Flats - with their own distinct styles.

      Giro d’Italia Bologna TT Course

      In conjunction with the Giro d’Italia, Zwift introduced their take on the 5.1 mile TT prologue and made it available on May 11, 2019 - the same day the pros were riding it. This is the first time Zwift has offered a Grand Tour course. The Bologna TT course is generally not included in the course calendar, but can be used for events.

      +

      Titan’s Grove

      In what might be the most beautiful portion of all of Zwift, the Titan’s Grove expansion pays homage to the Giant Sequoias of northern California and perhaps some of America’s other spectacular national parks. Situated in Watopia, right next door to Fuego Flats, Titan’s Grove provides plenty of visual stimulation over a twisty, hilly course through the woods. The road through Titan’s grove is also included in 5 new Watopia routes.

      +

      Yorkshire

      The most recent expansion is the addition of the Yorkshire world, centered around the 2019 UCI World Championship. Yorkshire includes 5 free-ride routes, including the Harrogate Circuit, raced by the pros during the UCI Worlds. The Zwift team added many nice references to Yorkshire history and points of reference.

      +

      World Choice

      On top of this expansion, you now have some more flexibility where you ride on a given calendar day. Zwift’s Guest World functionality was introduced in January 2019 and allows you to ride in your choice of two worlds on any day. Watopia is now always available as your “Home World”, and the other worlds (London, Richmond, Innsbruck, New York, Yorkshire, and Bologna) are rotated in the course schedule as “Guest Worlds.” London and New York tend to be on the schedule more frequently than the others, generally commanding about 2/3 of the schedule each month.

      Note that the Home/Guest World and course schedule is specific to free riding, and events such as races or group rides may occur on any course, regardless of which Guest World is on the course schedule. There is also a way to ride just about wherever you want, and we’ll talk about in an upcoming article, too.

      +

      Step Right Up and Win a Prize!

      Zwift is a game, and some of the things that make it fun are the various means Zwift uses to motivate you to ride. Zwift has added several new means of rewarding you for your hard work.

      New Route Achievement Badges

      For the “collect them all” kid in all of us, Zwift has expanded the collection of Achievement Badges to include new badges honoring the completion of 25 of the Zwift routes.

      +

      The Drop Shop

      Back in February, Zwift introduced the Drop Shop - a way for you buy new gear using a currency called “Drops” that you earn while you ride. Save up your Drops and you can buy new bikes and wheels to add to your virtual garage. Many of the major bike and wheel brands are represented, with prices ranging from 9,500 to 3,550,000 Drops for bikes, or 3,200 to 1,508,800 Drops for wheels. There’s a catch, though - you have to not only have the Drops in your wallet, you also have to have the Zwift riding experience. If you want a Cervelo P5x and have the 1,065,000 Drops you need to buy it, you’re going to have to reach level 45 first. The Drop Shop provides a fun way for you to choose your own prize.

      Levels 26-50

      Initially, Zwift offered 25 rider levels based on the amount of XP (Experience Points) earned by riding in Zwift. As many people were able to outgrow those first 25 levels, Zwift has now expanded to 50 levels. Each level allows you to unlock new kits, shoes, helmets, and other fun gear. To get to level 50, you need to earn 500,000 XP, which would be roughly 16,667 miles of free riding!

      +

      Technology Improvements

      In order to stay on top of the cycling game, uhh, game, Zwift and other cycling app providers need to be constantly improving the technology - providing robust support for new and existing trainers and sensors, improving the reliability of the connection, and solidifying the technology core in order to support more riders and more courses. There are many updates that have been made in that regard, and while these are often not very flashy, they are very important.

      Zwift for Android

      One pretty massive tech change that happened this year is the introduction of Zwift for the Android platform. This is not to be confused with the existing support for Zwift Companion on Android - this means that the full Zwift application is now also available for Android users, like it has been for iOS, AppleTV, and Windows PC users. From a development perspective, this is a big deal. From a customer perspective, this opens doors for all the folks who are using Android phones or tablets. The Android universe is vast and varied in regards to devices and their processing power. So far, Zwift has narrowed the list of supported devices down to some popular phones and tablets.

      Rolling Resistance Improvements

      Another subtle, yet significant effort has been in regards to rolling resistance. We love to talk and argue about which tires are the fastest on the Slowtwitch forums, and the Zwift team has the added challenge of coding to emulate the physics of the real world. Like the differences in aerodynamics of bikes, Zwift factors in the differences that the rider weight, tires, wheels, and terrain have on the overall rolling resistance. Basically, dirt is going to be slower than tarmac for the same effort, like it is in the real world.

      FutureWorks

      Last, but certainly not least is the recent introduction of Zwift FutureWorks, which Zwift describes as “a platform to soft launch innovations from the team at Zwift HQ.” The first innovation to come out of FutureWorks is steering, adding an extra level of realism to the game. To coincide with this, a Zwift Mountain Bike and a special segment of trail named Repack Ridge has been tucked away in Titan’s Grove in order to complete the experience. Look for more coverage of this new feature soon.

      +

      Whew! That was a lot to cover!


      As mentioned earlier, this is just covering some of the biggest things to occur in the last year or so on Zwift. We didn’t even cover the changes to racing and group riding. As you can tell, we have a lot more to cover - we’ll be working on several more articles over the next couple of months to help you get the most out of these and other Zwift features, so stay tuned!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Where_Are_the_Runners_on_Zwift_I_ll_Show_You.__7553.html b/site/slowtwitch.com/www/Indoor_Training/Where_Are_the_Runners_on_Zwift_I_ll_Show_You.__7553.html new file mode 100644 index 0000000..dcd1b80 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Where_Are_the_Runners_on_Zwift_I_ll_Show_You.__7553.html @@ -0,0 +1,650 @@ + + + + +Where Are the Runners on Zwift? (I’ll Show You.) - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Where Are the Runners on Zwift? (I’ll Show You.)

      + +
      +
      +
      +

      Almost everything we do can be done either as a singular or social experience. Including running. Solo runs are great, and social runs are great, but if it’s going to be a social event, dangit, it should be social, regardless of whether it’s actual or virtual.

      If you don’t know where the runners hang out on Zwift, it can seem as if there’s not very many of us. But there’s more than you think. You just need to know where they congregate. And I don’t just mean in the weekly group events, such as the popular Monday and Wednesday group Zwift runs. A lot of us don’t live in time zones convenient for these runs. What I’m writing about is where more runners tend to hang out, at all times and on all days, regardless of when you want to lace 'em up and go.

      +

      You may well already know what I’m going to write. If so, great! It might seem intuitive to you. But in fact the typical Zwifter doesn’t make a choice. Unless we’ve joined an event, when we boot up Zwift there’s a default place onto which we’re plopped, and that default doesn’t change unless we take an affirmative act. That default location, if you’re a runner on Zwift, is “The Flat Route” inside of Watopia.

      Zwifters know that Watopia is one of six “Worlds” (earlier this month our Travis Rassat showed us those Worlds) and every time you come to Zwift you’re given a choice of the day’s World or Watopia (see image below). Excluding the World Hack (covered elsewhere, and which is not available on the AppleTV I typically use to run the Game) you always have two Worlds to choose from on any given day, whether you're riding or running.

      +

      Let’s say you don’t want to just start running where Zwift chooses to place you. You can change Worlds (above, you have either Watopia or London available to you), and then change the Route inside of the World you choose to run in. The reason there’s a fair number of runners on The Flat Route is because it’s the default landing place for Zwift Runners. You’ve got to take matters into your own hands if you want to run where I and others are running.

      Most of the time it’s best just to remain in Watopia, because this World is always available, yes, but also because there’s a number of run-only routes. Here is where our Zwiftmaster, Travis Rassat, was not fully comprehensive in his listing of Zwift routes. Yes, he listed them all. For cyclists. But if you select Zwift Run, there are routes available that you don’t see when you select Ride.

      +

      When you’re just getting ready to commence Zwifting – Run or Ride – you’re given a choice of Worlds, and when you select a World you’re given a choice of Routes. If you’ve chosen to Run on Zwift you have Routes available that you won’t see if you chose Ride.

      Those run-only Routes include the 5K Loop, Jon’s Route, and several others. I believe there are 5 in Watopia, and each of these has offshoots (e.g., you can stay on the Main Road or turn onto the Beach Path, and here's a hint: I believe more runners choose the Beach Path). If you see a runner icon just after the name of the Route (as you’ll see above), it’s a run-only Route (no cyclists). This is where you’ll find a lot of those Zwift Runners you thought didn’t exist.

      For example, on a recent weekday morning I chose the 5K Loop. There were so many runners on that loop, I was pretty much always in the process of either passing or getting passed. I found it interesting to change my camera view, to look behind me, and see the runners coming up. The entire sidebar was full of runners – I didn’t count but I think there were 15 or 20, all within reasonably close proximity of me (within 5 minutes in front or behind). Compare this to London (below), where I dropped in for a run on a mixed Ride/Run Course and I’m the only runner in sight.

      +

      The shorter the route, the more runners you’ll see, because the density increases. You might see folks on the second or third rounder of the 5K Loop. When New York is the World of the day, the Shuman Trail Loop is very popular with runners. But you won’t see it on our list of New York Routes because it only shows up if you elect Run.

      When you look at the “Zwifters Nearby” sidebar on the right, you can easily tell how many runners there are. Obviously you can see who’s what, as in the image just above. If it’s a mixed route containing cyclists and runners that sidebar will show either a runner’s pace (8:45/mi) or a cyclist’s metric (2.0w/kg). That’s how you know who’s what in that sidebar, including a lot of people you can’t yet “see” on the road.

      +

      In the image above I’m a runner, bending over, doing my pre-run stretching (as we all do), and there’s a runner just in front of me, just logged on, ready to run. This is a mixed Route, cyclists and runners. Obviously there are many more cyclists, all of us getting ready to take off.

      In the image below, I’m getting ready to run, but there are only runners around because it’s is a run-only Route. This was on a weekend day, and I don't know yet but I get the sense there are more runners on the weekdays on the Routes I run. I’m still learning about the run-only Routes, and the turn options on these routes, and the times of day, weekdays versus weekends, to see what Routes are the most populated by runners, and when. I'm still exploring; I'm glad I don't know all the answers.

      +

      What you’ll see on the run-only routes is that you often run within sight of roads with cyclists. The image below is taken by me with my handheld as I’m running on the treadmill, so, sorry if it’s a little blurry. I’ve chosen the Invisible Man viewpoint (where I don’t see myself in the Game). There are two runners in front of me, and if you squint you can see some cyclists on the road to the right. These riders appear (just by my observation) appear to be actually in the Game. Real time. Further, there are a number of "run-only" routes that aren't entirely run-only, but run-only part of the time, and then they merge onto a Route shared with cyclists. (You'll see which are which as you test these out.)

      +

      It seems to me I'm not only with a lot more runners in the run-only Routes, but a faster class of runners. There are a lot of folks running in the 6s and 7s per mile. Faster than I was running. I did find that having them around me bent the trajectory of my run a little bit. I sped up a little, or if I could sense somebody was trying to catch me as my run was ending I’d slow up a little, to give him or her a chance. We were all in it together.

      And finally, I was called upon to fix my wife’s elliptical this morning after I finished my Zwift run. She doesn’t like running, but defaulted to the treadmill since the elliptical was down for repairs. She wanted to see what I’ve been running to, so I set her up via my Zwift account. Now she’s hooked, immediately texting all her workout friends about Zwift, none of whom are triathletes or committed runners (or runners at all… yet). I've created a Zwiftmonster, and now she and I have to share time on the mill. You and I are jaded. Multiplayer activations like Zwift are old hat to us, but the social workout experience without leaving the home is powerful and I got reminded of it today.

      I live in nature, by choice, but "in nature" means I run alone. Well, the dogs and I. My "social" runs are overwhelmingly on Zwift. Having a bunch of cyclists around is fine, but it's only a social run if there are other runners around. Zwift runs are where I get my social workout fix. Now you know where to find more of us.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Yes_You_Can_Change_Your_Smart_Trainer_to_12-speed_7646.html b/site/slowtwitch.com/www/Indoor_Training/Yes_You_Can_Change_Your_Smart_Trainer_to_12-speed_7646.html new file mode 100644 index 0000000..4c916b4 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Yes_You_Can_Change_Your_Smart_Trainer_to_12-speed_7646.html @@ -0,0 +1,646 @@ + + + + +Yes, You Can Change Your Smart Trainer to 12-speed - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Yes, You Can Change Your Smart Trainer to 12-speed

      + +
      +
      +
      +

      We’ve been asked this question on our Reader Forum. Can you make your smart trainer 12-speed compatible? Yes. It’s easy, and it’s not that expensive. It’ll cost between $79 and $89 to get what’s called an XDR driver body.

      What’s the use case here? I’ve decided to put my gravel bike on my Saris H3 trainer. That bike has a SRAM AXS 12sp system with 10-tooth 1st position cog. The H3 trainer has what your trainer almost certainly has, if you’ve got a smart trainer: a driver body that’s made for an 11sp system, with a 1st position cog no smaller than 11 teeth. What you need to do to adapt your trainer to a SRAM 12sp system replace the driver body.

      +

      You can buy new wheels today from just about every wheel maker with an XDR driver body on the hub. You spec this when you buy the wheel. You can also change out the driver body of your existing 11sp wheelset to match the new SRAM AXS drivetrain on the bike you just bought. Probably. We’re working now on a comprehensive chart showing which wheels from every company are upgradeable to an XDR driver body.

      But we’re limiting this, today, to changing out your driver body on your smart trainer. Most Saris, Tacx, Wahoo smart trainers can take an XDR driver body, and all these companies sell their version of the driver body. Just, check with your smart trainer company if that trainer is older than, say, 2 years old, to make sure it’s upgradeable.

      Above is a pic of what you get when you get sent a new driver body. Below is my badly lit, badly produced video showing me changing out the driver body on my Saris H3.

      +

      +

      Fortunately, my video isn’t very long! I hope you can see how easy this is. As for tools, basically, can you change a cassette? Do you have the tools for that? A chain whip or chain pliers. (Chain pliers better, but chain whip used in this vid.) You need a cassette spline tool. That’s it, in terms of bike-specific tools. Okay, a cone wrench. But that’s not really a requirement. You should be able to muddle through with a set of open/box-end wrenches or a crescent wrench (adjustable spanner for our UK readers).

      That’s it! Simple. Happy riding.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Zwift_A_Potent_Tool_for_Training_6689.html b/site/slowtwitch.com/www/Indoor_Training/Zwift_A_Potent_Tool_for_Training_6689.html new file mode 100644 index 0000000..6393e53 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Zwift_A_Potent_Tool_for_Training_6689.html @@ -0,0 +1,657 @@ + + + + +Zwift: A Potent Tool for Training - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Zwift: A Potent Tool for Training

      + +
      +
      +
      +

      [Note: This article was originally published in Autumn, 2017, and has been updated to reflect changes over the past 12 months. This is the 5th of what will be 8ish articles on how to Zwift; the list of articles, in order, is at the bottom of this article.]

      For many of you, half of the first 4 installments in this series were blah, blah, blah. You have no interest in group rides or virtual racing. You’re just interested in Zwift’s workout mode. So let’s get to it.

      Zwift is solid as a workout tool, with power-based training using predefined workouts and plans, custom workouts, or group workouts.

      +

      What’s this “eff tee pee” stuff everybody’s talking about?

      If you are completely new to power-based workouts, Zwift offers close to 700 predefined workouts. You’ll need to input your FTP; and select your workout. We haven’t mentioned FTP (Functional Threshold Power) yet. For workouts to really be effective and appropriate for you, you have to have at least some idea of what your FTP is, so let’s figure this out.

      The best way is with an FTP test, so that might be your first official Zwift workout! If you’ve been riding Zwift already, and have put in some solid longer efforts, Zwift will recognize the efforts and estimate your FTP. Alternatively, if you already know your FTP, you can enter it manually.

      +

      Fire up Zwift like normal, pair your sensors, and go to the Start screen. For the Ride Type, click the Select Workout button, and you will see a screen similar to this:

      +

      The workouts are listed and grouped on the left side. At the top of the list, you’ll see options like FTP Tests and workouts grouped by duration. A little further down, you’ll see different training plans available. You can expand any of these categories to see the workouts within them.

      There’s a lot of information here. In my opinion, selecting the plan you are going to be riding for the next few weeks – what is what you’ll be asked to do – comes quick in the menu process. I’d like to have a better picture of the plan before I jump in. A quick Google search for “Zwift workouts” will reveal an unofficial Zwift website that includes a much more detailed view of the workouts and plans so you can make an informed decision.

      Once you’ve selected your workout, you are returned to the Start screen. Select your course, and select Ride. We are now ready to start our workout, but we’ve neglected one thing; we need to set our FTP. One minor gripe I have about Zwift is that I can’t get to my profile information, settings, or user customization from the Start screen. You have to start a ride, and then pause it right away to change any of these.

      +

      To set your FTP (or confirm it), pause Zwift, and select the edit icon next to your name. This will bring up the User Profile screen. You can enter or confirm your FTP and other information here.

      +

      Select Save to return to the Pause screen, make any other desired changes in Settings or User Customization (you’re wearing your Slowtwitch kit, right?) and select Back to return to the ride. Now we’re ready to work out!

      +

      This looks a little different…

      The Zwift HUD adapts for workout mode, and is more focused on your workout information than the course information. On the left side of the screen you’ll see a list of the intervals of the workout, which are color-coded based on the workout zone for that interval. You will also see some stars next to each interval - in workout mode, you will earn experience points (XP) based on how well you hit your targets rather than the distance traveled.

      At the top center of the screen, the display is changed to provide more cues on your current power target and output, along with messages to indicate what you need to do to hit your targets. Zwift will provide targets for power and cadence, it may occasionally tell you to increase or decrease your power or cadence.

      We have not talked much about smart trainers, but this is where a smart trainer makes this a bit easier. If you are using zPower, you will have to do some shifting to hit your desired numbers. With a supported smart trainer, the resistance will increase and decrease according to the workout, and you can just focus on hitting the cadence numbers.

      If you are using a smart trainer, you will also notice that the workout target power takes precedence over the course itself - if you are on a hilly course, you won’t feel the hills like you would in regular riding mode. If your workout has you holding 200 watts for 5 minutes, Zwift will tell your smart trainer to hold 200 watts, regardless of the grade of the road. Your speed will be adjusted accordingly.

      You will also see a few more changes on screen. Messages can be displayed in the middle of the screen. You will see your target and actual power at the bottom of the screen. You will also notice a thing floating in front of your avatar. You may have noticed other riders with these when riding in Zwift, and this is an indicator to everyone else that you are doing a workout.

      +

      After your workout is done, you will see your ride report. If you choose to continue riding, you will be placed back into the normal “Just Ride” mode.

      +

      What if Coach Told Me I Need to Do my 2 x 20 Workout?

      If you’re more advanced in your knowledge of power-based training, or are working with a coach who prescribes your workouts, Zwift offers a custom workout creator. Please note that you have to use a PC or Mac to create workouts. You can access your custom workouts from an iOS device once they are created, though there is a bit of a process.

      Creating a workout is straightforward: Navigate to the “Select Your Workout” screen like you would for a standard workout on your PC or Mac. You will see a link at the bottom of the panel that says “...or create your own.” Alternatively, you can scroll all the way to the bottom of the workout list, and you’ll see the “Create a new Workout” button.

      +

      You will now see the New Workout screen. Zwift provides some visual cues to get you started, and the drag-and-drop interface makes it easy to create and adjust your workout. You can name your workout, add a description, add new blocks to the workout, and tailor each block as needed. If you click on block of your workout, you can reshape that block by changing the starting and ending target power and/or target cadence. You can also change the number of times an interval is repeated and add messages that will be displayed during your workout.

      +

      Save your workout, and you will now see it in the list of available workouts. You can edit, delete, or copy your custom workouts. If you want to share your workout with others, or use a workout that has been shared with you, you can use the actual system file. On a PC, this will be found in your documents ? Zwift ? Workouts folder. Each workout is stored as a .zwo file.

      If your coach has Zwift, they can create the .zwo files for you, you can drop them in the Workouts folder, and you’ll be able to select them for your next ride. Like any ride in Zwift, at the end of your workout, you have the option to upload your performance to TrainingPeaks, MapMyRide, Strava and others, so you can show your coach what you accomplished.

      +

      A Brief Look at Group Workouts

      If you like a group experience, Zwift offers group workouts as events. You can go to Zwift’s Events page to find upcoming group workouts. Like race or group ride events, you just choose to join your group workout at the Start screen. If you get there early, you’ll be warming up on a virtual trainer like in the image above.

      Once the workout starts, it’s very similar to a group ride, with one difference: “rubber banding.” While a group ride requires you to keep the specified pace or get dropped, a group workout is a bit more forgiving. Riders of all capabilities can ride together in a group workout, and as long as you are trying to hit your intervals and pedal, you will stay with the group.

      +

      I found the group workout to be quite a bit more fun than a group ride - since we stayed together better, there was a bit more camaraderie. We were texting messages back and forth, and at the end, it was raining Ride Ons - everybody was giving each other the thumbs up. It was fun.

      But it wasn’t just fun. Our publisher is in the middle of a series on the Most Important Elements for success (as defined by podium placings) in triathlon and, without giving too much away, and hoping not to seem overly cryptic, the denouement of this series will speak to the importance of this Zwift feature.

      Slowtwitch Group Workouts

      Groups Workouts on Zwift equals Structured Training on Zwift, if you engage in a Structured Training event (as opposed to simply using Zwift solo, on your own). Slowtwitch has, as of this writing, 4 weekly "events" on Zwift, and 2 of these 4 are Group Workouts. They are Slowtwitch Structured Training (Tuesdays, 7pm Eastern); and Festina Lente (Thursdays, 5:30am Eastern). We're about to add some more weekly events, including more Structured Training Events (Group Workouts).

      +

      The Sky’s the Limit

      Power-based workouts take Zwift from being entertainment to being a serious workout tool. For those who want to jump in and get some good workouts in, Zwift has close to 700 different workouts amongst a dozen different plans that can get you off to a strong start. For those who are inclined, custom workouts mean the sky’s the limit. Group workouts add another dimension of fun. We’ll be looking some of the other indoor training software soon and see how Zwift stands amongst its peers.

      +

      Here's the whole series:

      1. Welcome to Watopia: Install Zwift using virtual power, the most cost-effective option.
      2. Zwift, The Deeper Dive: One more layer below the surface.
      3. It's Game On With Zwift: Zwift is a game, but also a virtual community.
      4. Zwift: Let's Dance! The social aspect; Zwift is a Massive Multiplayer Game.
      5. Zwift: A Potent Tool for Training: Zwift’s workout mode.
      6. On Zwift Every Day is Race Day: Zwift's racing culture and community.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Zwift_Foibles_6762.html b/site/slowtwitch.com/www/Indoor_Training/Zwift_Foibles_6762.html new file mode 100644 index 0000000..16aa4d3 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Zwift_Foibles_6762.html @@ -0,0 +1,613 @@ + + + + +Zwift Foibles - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Zwift Foibles

      + +
      +
      +
      +

      Nobody was more averse to indoor cycling than me. And yet, every Tuesday and Thursday I look forward to heading into my workshop for 75 minutes of riding indoors by myself. Why? Because I like riding with others.

      Now I'll bet that doesn't make a lot of sense. But it does in Zwift's world.

      I'm a month into this new indoor venture and you might think I slid right into it. Not even close. Tech like this is naturally hard for me. In fact, the first time I rode on our own Tuesday training ride I inconvenienced all 80 or 90 Slowtwitchers by making them stop — twice — because when the "gun" went off there my avatar stood, straddling the bike, one foot on the ground. What a moron! (Me, not my avatar.)

      +

      Now that I've successfully negotiated a half-dozen of our regular Slowtwitch group events I feel I have a nuanced perspective. I am sort of computer dyslexic. I'm not actually dyslexic, but I suffer from something like it whenever a computer is involved. Computers are wholly unintuitive to me. Odd, since I spend so much time on them. If you're like me what I write below might resonate and make your Zwifting a little easier to wrangle.

      The Basics

      You need a few things to Zwift. First, you need a trainer. Pretty much any trainer will do. You need a computer, or a cell phone, or a tablet. (As I describe further below, best to have a computer and a cell phone.)

      You need something that "speaks" in the language of power (watts), which might be your smart trainer, or a power meter on your bike, or a $70 thingy called a "Speed and Cadence Sensor". (Here's an article on how to get up and running on Zwift using just a dumb trainer and a speed and cadence sensor.)

      And finally, the thing that speaks the language of power — whatever it is — must communicate with your computer, in one of two languages: ANT+, or Bluetooth. Which sets the stage for my first facepalmworthy moment.

      Connectivity

      I knew I had my first Zwift group ride coming up, and I frantically sent off for an ANT+ dongle to stick into the USB port of my Apple computer, so that my smart trainer I got (a CycleOps Hammer) could "talk" to my computer wirelessly. I got the dongle! And it all worked! And then it hit me. My computer has Bluetooth. I didn't need the dongle. My CycleOps Hammer communicates a signal both in ANT+ and in Bluetooth. Me, the genius.

      You can use that dongle, which allows your computer to "hear" an ANT+ signal sent from your smart trainer (or power meter, or speed and cadence sensor). Just, if the Zwift program is sitting on your iOS cell phone or tablet or a fairly recent Mac, you probably don't need that ANT+ dongle. Your handheld device or your Mac probably "hears" a Bluetooth signal.

      You will need the dongle in two cases. First, if the device sending the signal only sends an ANT+ signal (I think that's the case with a lot of bicycle power meters). Second, you cannot communicate on a Windows desktop computer via Bluetooth. Zwift won't do that. At least not as of this writing. You would need that ANT+ dongle (I spent $18 for mine) if a Windows desktop (or laptop) is where you have your Zwift app installed.

      Mobile or Desktop?

      Zwift is a program. You have to download it. You can download it onto any of the devices I mention above: Windows, Mac (OSX), or iOS (Apple phone). Which is best? The solution I've settled on is running it on my Mac laptop (Macbook Air). I use both my laptop and iPhone 6S simultaneously when I ride on Zwift. Simultaneously? I'll bet that sounds confusing. (I'll unconfuse this in a moment.)

      +

      Down in my workshop, where I'm set up, I've got an HDMI cable running from the laptop (bottom left in the image above) to a big screen and, really, if you can get your Zwift screen to run on a big screen, great. Then you can see things better. There are cheap adapters you can run from your phone, tablet, laptop directly to a big screen.

      The Companion Mobile App

      But! — and this tripped me up — I didn't understand Zwift Mobile Link. There are two programs or apps you need to download for best effect. First, the Zwift program. This is the engine that runs Zwift. Not required, but pretty darned helpful, is this second program called the Zwift Mobile Link. This is not the Zwift Program, per se. This is a little helper application. What does it do? Zwift says it, "turns your mobile device into a remote control for Zwift," and I think that's a very good description.

      For example, let's say I'm Zwifting. Let's say I'm running Zwift off of my laptop. If I want to (for example) text somebody during my ride, I could do it. But I've got sweaty hands, I'm trying to type out a text, as I'm pedaling, not ideal. However, if I boot up this second program — Zwift Mobile Link — on my iPhone this app actually knows that I'm on a Zwift ride; it recognizes the ride; and I can do all my texting for that ride using the Mobile Link. Just, it only knows I'm Zwifting if the device running the Mobile Link app is on the same network (hooked up to the same wifi) as the computer running Zwift.

      This is why I say that when I Zwift I'm using both my laptop and my iPhone simultaneously. But I didn't understand this at first. Jordan (Rappstar) tipped me to texting on the iPhone while actually using the Zwift program on my laptop. So I booted up the Zwift program on both. That caused no end of computer confusion. When I told Jordan about this later he said, "No! Don't have two versions of Zwift going at the same time!" Jordan still does not understand how utterly capable I am at doing things that are intuitively nonsensical to the typical person.

      +

      See the screenshot of my iPhone screen above? The orange Z icon is the Zwift program. This never needs to be running on my cell phone, because I've got the Zwift app itself running on my laptop computer. That teal Z icon, that's what I want to have running as a companion app.

      The other thing the Mobile Link app does is make it much easier to find and join a ride or race or any kind of organized "event" in Zwift. It took me awhile to understand how you can "sign up" for an event. You can find events via Zwift's website. You can log into your account on the website. But the website is pretty limited. While you can "see" upcoming events on the website, read the descriptions, you can't join or sign up for them there. The easiest way to sign up for an event is on the Mobile Link app. This took me at least a week to figure out (because of my computerlexia). Let's talk for a minute about how utterly confused I was about how to interface with Zwift.

      Zwift.com, Zwift (the Game), Zwift Mobile Link (the Remote Control)

      If you go to Zwift.com, you can do certain things there. You can establish an account, set up ways to pay, log into your account. When you're logged into your account you can see your activity, the rides you've done. You can also read support articles. You can download the program Zwift from the website. You see "events" in the calendar from the website. But you cannot join these events from the website. And, you cannot "Zwift" from the Zwift website. You can download the program Zwift, install it on your computer (or tablet, what have you). That's it. That's what you can do on the Zwift's website.

      Then there's the program Zwift, which you now have on some device (iOS phone or tablet, or desktop Windows or Mac computer). Zwift refers to the program, the application, as the "Game", i.e., if you execute a task, such as ride your bike, in the application you're doing it "in the Game." The Game isn't available for Android devices. But the Mobile Link app is. So, you could be running the Game on either Windows or Mac, while running the mobile app on either iOS or Android. Whatever runs the Game has to be fairly robust (decent graphics card, decent amount of RAM, etc.). Now you can Zwift.

      If you want to enter a group ride (which Slowtwitch has on Thursdays), or a group workout (which we have on Tuesdays), you have to "find" that event and that's not so easy to do from "the Game". In other words, you can find events easy on Zwift's website, but not enter them. You can enter them on the Zwift program, but it's not easy to find them. You can enter an event while in the Game, but only if the event is coming up soon, within the hour, or within a very few hours perhaps.

      +

      The Zwift Mobile Link app makes certain tasks much easier! Such as entering an event. You can enter any event (ride, workout, race) that's on the schedule, even if it's a week from now. Look at the image above. There are three views of my iPhone screen when in Mobile Link. At left is what one sees when clicking the EVENTS tab at the bottom of the screen. I've migrated to this upcoming week's Hilly Vanilli ride on Thursday (now at 11:30am Pacific because of Daylight Savings). When I click on this, I get the screen image at center. I don't want to enter as a "C". I want to enter as a "D" (because I'm slower than a C). So, I scroll down and there's my ride, the D ride, and when I enter that + sign changes to a checkmark. See? I'm entered!

      For some time I couldn't remember what signified having "entered"; the + sign or the checkmark. You click the event, turning a + sign into a checkmark. Checkmark = entered. You may need to select your level (A, B, C, D). As you see, I checkmarked D.

      Here's a bit of alchemy I don't understand. If you click this link from a desktop computer it'll take you to the Hilly Vanilli (our Thursday ride, which I love). If you click that link from a mobile device, and you have Zwift Mobile Link installed, it'll take you to that screen above, and you can click and enter right there.

      In the Mobile Link app you can text people, give them a thumbs up, take a picture of any moment inside the Game, you can look at any of your ride metrics, all kinds of stuff! It's pretty clear that Zwift's intention is to further empower this Mobile Link App. Yes, your laptop might be where the Game sits, but the Mobile Link app is where Zwift wants you to control all the Game functions and why not? It's much easier to do stuff on your phone, including voice messaging during a ride. Which is another thing it took me a week or two to figure out. It works just like voice text messages. Which can be pretty funny on the screen, during a ride. Last week Jordan texted to the group to "honor your trainer", which is not what he said, but it's what his phone thought he said. And besides, I think my trainer does deserve some respect.

      Your Settings

      There are three different places your settings live, and boy did this stymie me for awhile! On the website, as noted, you have your basic account. Payment history, payment type, your gender, weight, height, login credentials. Then, inside the Game, there's a MENU, and if you click MENU you'll see the screenshot just below, with "settings" at the bottom right. But this is just your preferred Game settings, imperial versus metric, stuff like that. In the upper left you'll see a pencil icon, and if you click that, it mostly includes the settings you established when setting up your account (gender, height, weight). But there are two other settings not available to you while logging into your account on the website: Max HR, and FTP.

      +

      This FTP setting is really important if you do our weekly Zwift workout on Tuesday, because the workout is likely to key off FTP, as in, 5min @ 110 percent of FTP, then 2min @ 60 percent of FTP. Boy are you in for a rough go if you don't have a reasonable number stuck in that box! Boy did I struggle before finding that box!

      Finally, I'm going to talk a little about ERG Mode, because if you're doing a Zwift workout (instead of a ride, or a race), which is what we do Tuesdays, these are typically done in ERG Mode.

      ERG Mode

      If you're on a smart trainer you have a thing available to you called ERG Mode. I know what ERG or Ergometer Mode is because that's been a staple of Computrainer for a quarter century. When Zwift is in ERG Mode you don't need to worry about the resistance. ERG mode controls the resistance automatically. On Tuesdays Slowtwitchers meet at 4pm (Pacific) for a "Structured Workout" and this takes place almost entirely in ERG Mode. The workout is often based on your Functional Threshold Power (FTP) which is about how many watts you can push continuously for an hour. The workout might call for 60 percent of your FTP as a warmup, then we're going to do sessions at 90 percent of FTP, then 110 percent, back to 60 percent for awhile and so forth. As long as you have your FTP loaded into your Profile, you can hang with the whole group.

      Last week I was riding in the same group with Dan Stubleski. My hard efforts, at 90 percent of my FTP, were ridden at the same watts as his rest efforts. His power was literally half again (or more) higher than mine, but because this group workout was gauged to each person's ability, we all stayed together.

      Even then, I had a hard day. I just couldn't do the workout. Remember, the computer controls the effort when in Erg Mode. At a certain point I just couldn't ride 110 percent of my FTP. You can't just pedal slower in ERG Mode. Your smart trainer knows that power (watts) is torque x cadence, which means, if you lower your cadence your trainer will simply increase the torque.

      So, I stopped several times during the workout.

      This is the hard way! If you don't want to be forced into a workout you just can't do, you have two options. First, put a smaller number into that FTP box in your Rider Profile. Look, it's not your browsing history. Nobody is going to know. Nobody is going to care. Put a smaller number in there, and if the workout is too easy then put a bigger number in there next week.

      The second thing you can do is just click out of ERG Mode. If you click anywhere on the screen, while in the Game, a bar full of icons pops up, which allows you to do a bunch of stuff. If you're using a smart trainer, one of those options is to click out of ERG Mode. If you just can't do the workout, click out for awhile. Then click back in when you think you're recovered. If you keep pedaling you'll still be riding with the group on these group workouts.

      Just remember, clicking in and out of ERG Mode, messaging other riders, while you can do all that stuff via that menu that pops up inside the Game, it's typically more easily done via the "remote control" you're holding in your hand, which is concurrently running Zwift Mobile Link.

      Besides all that!

      I couldn't figure out how to wear the Slowtwitch jersey. I was in my own group ride, not wearing my own jersey, tho everyone else was. I had trouble saving my sessions after I finished. It took me 5 rides to finally understand the times on the "leaderboard" to the right of the screen during the Game, that everyone ahead of me was that many minutes and seconds ahead of me, and everyone behind was that many seconds behind me. I'm still a little fuzzy on how to message someone privately rather than messaging the entire group. Mind, almost no one else has these problems. This is how utterly computer illiterate I am.

      Zwift is a runaway success, and I get the sense that part of my struggle was me; and part of it was Zwift running around with its hair afire trying to accommodate five thousand folks who're riding on that platform at any one time, 24/7. My guess is that, at some point, when there are sufficient features to keep us all satiated, those who find everything about computers opaque (me!) will find Zwift an easy platform to navigate. That said, I think I've mostly got my arms around Zwift. So that I can give it a big hug.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Zwift_It_Do_Run_Run_Run__7490.html b/site/slowtwitch.com/www/Indoor_Training/Zwift_It_Do_Run_Run_Run__7490.html new file mode 100644 index 0000000..7970a16 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Zwift_It_Do_Run_Run_Run__7490.html @@ -0,0 +1,657 @@ + + + + +Zwift: It Do Run Run Run! - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Zwift: It Do Run Run Run!

      + +
      +
      +
      +

      With the popular 100/100 Run Challenge starting in a couple of weeks, some of the members of the Slowtwitch community will be hitting the good ol’ “dreadmill.” Why not make it a bit more fun? While Zwift is best known as an indoor cycling platform, it also offers a fun venue for runners, too. You don’t have to have a bike to explore and enjoy Watopia! Let’s take a look at what Zwift running is all about and how you can get started.

      +

      The Running World Meets the Virtual World

      With running, Zwift is tapping into an entirely new group of people, which has the potential to be huge. You no longer have to be a cyclist to be a Zwifter - a runner with a couple of pieces of equipment can subscribe to Zwift and never touch a pedal in Watopia. Zwift offers runners many of the same features that cyclists love about Zwift. The worlds and routes that thousands of cyclists ride every day can are available for runners too, and there are even a few run-only routes. Running workouts and training plans are available. Races and group runs are available for runners of all levels. The Zwift runner can also benefit from many of the game-like features that their cyclist counterparts enjoy, such as being able to level up, unlock new virtual running gear, and chase fun challenges and achievements. Like it has done for cyclists, Zwift offers a lot of different features to motivate and entertain runners.

      In the indoor cycling world, we’ve seen a massive improvement in the equipment used to support the indoor cycling experience. In the last decade, cyclists have benefitted from advancements in the quality and accuracy of power-based exercise, with a wide range of smart trainers and indoor bikes now available to support software applications like Zwift.

      The running world is a bit behind in this regard - the idea of running with power is still fairly new, with running power meters such as STRYD having only been on the market for a couple of years. Runners have other sensors - such as accelerometer-equipped footpods and heart rate monitors - that have been doing the job for years. Zwift can read certain types of these sensors and use that information to display metrics and move your avatar on screen. Running in Zwift is roughly equivalent to cycling in Zwift using a classic trainer and zPower: It’s taking input from devices that send a signal and translating that to the virtual world.

      What Zwift can’t do yet is control the devices that send the signal like it can for cyclists. While cyclists with a smart trainer can feel an increase in resistance as they climb a hill in Zwift, or experience the incline change when using an accessory such as Wahoo’s KICKR CLIMB, runners won’t feel a difference if they are running up a virtual hill. Zwift is simply receiving the speed at which the runner’s feet are moving and using that to drive the virtual run experience. If you want your avatar to go faster, you have to set your treadmill to a faster speed. If you want to experience a hill climb, you’ll have to adjust your treadmill yourself.

      +

      Some Assembly Required

      Now that we have at least a basic expectation of running in Zwift, let’s jump in and do it! Zwift has a handy web page that explains everything you’ll need to get started.

      The run sensor is going to be the heart of the Zwift running system. Zwift currently supports just a handful of specific sensors. They offer their own RunPod, or you can use footpods from STRYD, Polar, or Garmin, shoes from Under Armour, and certain treadmills. These devices transmit run speed information using Bluetooth Low Energy (BLE) or ANT+ signals to the device where Zwift is installed. As mentioned earlier, Zwift can’t control the treadmill’s speed or incline, but can listen to these devices and behave accordingly.

      The run sensor is the transmitter and the Zwift software application is the receiver. The Zwift app needs to be installed on a platform that not only provides the visuals, but also supports the communication between the run sensor and Zwift. Zwift can run a wide variety of platforms:

      - Windows PCs (Windows 7 or higher)
      - Android devices (smartphones or tablets with Android 6.0 or higher)
      - iPhones (iPhone SE or iPhone 5S or newer, with iOS 9.0 or higher)
      - iPads (iPad Air or higher, iPad Pro, iPad Mini 2 or higher, or iPod 6, with iOS 9.0 or higher)
      - AppleTV (4th generation or higher, including AppleTV 4K)

      The good news is that you probably already have a device that will let you run Zwift. Any of these will do the trick, with the key ingredient being the communication protocol - the device needs to support either Bluetooth Low Energy (BLE) or ANT+. For the most part, any of the Android, iOS, or AppleTV devices listed above will support BLE, natively. Windows PCs may support BLE natively (especially laptops), but may need a bridge (or “dongle”) to provide the necessary support. For ANT+ devices, there are very few devices that support ANT+ natively, so you will likely need a dongle.

      Finally, you’ll need a place to run. This is likely going to be a treadmill. The neat part about all of this is that it makes for a fairly portable setup. With a run sensor and your phone, you can run in Zwift on a treadmill at home, at the gym, or at a hotel fitness center.

      +

      Alright, let’s run!

      Once you have the Zwift application downloaded and installed (allow yourself at least 30 minutes for the download, user account setup, and installation), we can fire up the app, pair the sensors, choose our route and/or workout and run. For this article, we’re using an iPad Mini with a Wahoo TICKR X, which technically isn’t on the list of supported devices. Nonetheless it works well, once you get Zwift to recognize it. We’ll touch on that in a moment.

      Once you’ve installed Zwift:

      Log in to your account. Zwift is an MMO (Massively Multiplayer Online) game, which requires an Internet connection and an account. You can set up your account on Zwift’s website, or you will be prompted to enter your information the first time you use Zwift.

      +

      Choose the option to Run. After you log in to your account, Zwift will always prompt you to pair your devices. If you ride and run in Zwift, you will set up two sets of devices, and it will look for the same devices the next time you want to work out. Select “RUN”.

      +

      Pair your running devices. Initially, you won’t have all blue boxes as shown above. You’ll see orange boxes, and by selecting each one, can pair your sensors. If you’re using a heartrate monitor, you can pair that at this time, as well.

      For those using a TICKR X, you have to do a little detour in order to get Zwift to recognize it.
      Install the Wahoo Fitness app on your device.
      Open the Wahoo Fitness app, recognize the TICKR X, make sure it is working, and then close the Wahoo Fitness app.
      When you return to Zwift, Zwift will now recognize the TICKR X so you can pair it.

      +

      Calibrate your device. Zwift provides a tool that will have you run at 3 different speeds in order to provide the most accurate measurement possible. Follow the on-screen prompts, and if everything goes well, you will see a success message and return to the Device Pairing screen.

      +

      Select your route. Once your sensors are paired and calibrated, you’re well on the way to doing your first Zwift run - you just have to decide where! As shown above, you can choose your route and/or workout, join a race or group run, or catch up with a friend. For our purposes, we’ll go for a run in Central Park, since New York is available today. Select the “NEW YORK” tab at the top of the screen, and select the “ROUTES” button.

      +

      In each of Zwift’s worlds, there are several routes options. Most are also cycling routes, but there are a few runner-only routes. Select your route and select “OK”.

      +

      You’ll return to the route/run selection screen where you can select “RUN”. You should see something like the image above. Choose the desired speed on your treadmill and your avatar will move accordingly. That’s it - you’re running in Zwift!

      +

      When you’re done with your run, you can just touch or click your screen, and a “MENU” button will be displayed in the lower left corner, where can end your run and view or share your stats.

      +

      Wrapping it Up

      And there you have it - the start of your Zwift running career! We’ll touch on some more features of Zwift running soon, but in the meantime, Ride, err, Run On!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Zwift_Let_s_Dance__6678.html b/site/slowtwitch.com/www/Indoor_Training/Zwift_Let_s_Dance__6678.html new file mode 100644 index 0000000..37ab359 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Zwift_Let_s_Dance__6678.html @@ -0,0 +1,651 @@ + + + + +Zwift: Let's Dance! - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Zwift: Let's Dance!

      + +
      +
      +
      +

      [Note: This article was originally published in Autumn, 2017, and has been updated to reflect changes over the past 12 months. This is the 4th of what will be 8ish articles on how to Zwift, the whole series binge-published over the next 2 weeks.]

      Our series of stationary training articles dedicated to Zwift has been about getting the most out of the individual experience. But what about those of you who enjoy the social aspect of riding a bike? Or think you might?

      Zwift differentiates itself from most other indoor training software tools in that it is an MMOG: Massively Multiplayer Online Game. You’re not alone in Zwift’s virtual world.

      +

      This can be a little intimidating. Joining an online group session is only slightly less so than joining your first road group ride. It’s not uncommon to feel like that socially awkward pimply-faced freshman. Zwift is that slightly-more-popular-than-you friend that pushes you out onto the dance floor when your song comes on.

      Like other indoor training software tools, Zwift will certainly provide ride feedback like other indoor training tools as part of the Zwift Head’s Up Display, which we covered in our second article. But when you first start riding in Zwift, your focus is immediately drawn to the virtual world, with your avatar front-and-center. Not only do you see yourself riding, but you see other people on the course with you. You are no longer alone. You can interact as part of a real time, worldwide community. You’re on the dance floor and you’re dancing.

      +

      Your interaction with others in Zwift can be whatever you want it to be, from incidental contact to being the life of the virtual party. Wallflowers can keep a safe distance, limiting themselves to observing others on the road. At any given time, there can be thousands of people riding in Zwift, from all over the world. As you get closer to other riders you’ll see their names and their home countries displayed near their avatars, and in the rider list on the right side of the screen (above).

      Feeling socially warmed up? You can wave to other riders as you meet them, send group text messages, or even give another rider a “Ride on!” You may even see one of the popular schoolmates (we’ll talk about running in Zwift soon, too):

      +

      Zwift allows not only this casual contact, but with a bit of planning you can be part of a more discreet, targeted effort such as a group ride or workout. Zwift offers events that take the social aspect of indoor riding up a few notches. Let’s dig into “events” a bit.

      Alright, where do I RSVP?

      The good thing about Zwift is that you don’t have to RSVP to the party; you just show up. You just need to know what is happening and when. The best place to see this is at www.zwift.com/events:

      +

      The Zwift events website lets you see what's coming up soon. You can click on any of the events to get more details. There are events going on all around the world, and you just need to determine which work for you, and be there at that time. There isn’t necessarily a sign-up. It’s like the local group ride, you just gotta know when and where.

      Zwift events can be broken into 3 categories: races, group rides, or group workouts. Read the descriptions; you’ll can get an idea of how fast you’re going to need to go to keep up. While your local real group ride may specify a pace of X mph, in Zwift it’s usually dictated by watts/kg: You will be riding with people at a similar power-to-weight ratio as yourself. (There are some rides that are velocity-based.)

      +

      Some of the events do take a bit more planning than others. For example, a race may have you indicate your class by changing your name on screen to include your category or other details. For example, the race in the image above has a link that will describe more about how to qualify, as well as the rules around the race.

      When your event is approaching, you just fire up Zwift like normal. At the Start page, you will see the Upcoming Events display on the right side of the screen, as shown here:

      +

      Just click on your selected event and you’re prompted to join. You are now ready to go. If you start early, you will be sitting at the start line with a group of other riders warming up on virtual trainers (as is the case just below).

      +

      Interaction in the group ride is a bit different than in the open ride. There will be designated ride leaders and sweepers, which are indicated by arrows floating over them and a colored beacon that you can see from all over. All group messaging will be limited to the group ride participants. As you head out on the course, you will see other riders, but will not see their names show up in the rider list on the right side of the screen. You will see your position within your group. The only riders that will be listed will be your fellow group riders. Riders in the group will all be wearing matching kits.

      Once the clock hits zero, your trainer disappears and you will take off as a group. While it is a group ride, there is nothing that will normalize all of the riders so you can stay together. You can get dropped (and, boy, did I!).

      +

      A group workout is a bit different than a group ride in that it will keep all the riders together. So, whether you are cranking out 200 watts or 500, you will stay together unless you stop pedaling and drop a few seconds behind the group.

      How fun are group rides? Let me ask you this: How fun are analog, on-road group rides for you? If you find them frustrating, what can I say? Zwift is pretty realistic. You may find a similar dynamic. Virtual group riding adds some interesting elements: maintaining pace and keeping up with the group, using the draft, taking the front, etc. That’s not so easy, and it’s especially tricky in a virtual world.

      Give this some time. Don’t be discouraged if you don’t yet have the hang of group rides, even after you have a few of them under your belt. In the screenshot above, we were expected to be pacing at 2.0 watts/kg and yet I was losing pace to the main group while cranking at 4.0 watts/kg. How did that happen?! The groups set their wattage and it’s suggestive. Also, from a 50kg female's perspective (so I've heard), try riding 2.5w/kg ride on a flat route and you’ll realize, much like in real life, you actually need to put out more watts just to keep up.

      My group ride experiences were pretty varied, and maybe it just takes some practice, and perhaps experience in choosing the right rides, with the right riders! My first ride was supposed to be a 16-mile ride at what I thought would be a manageable pace. I was dropped within the first 3 miles! On the next ride, 5 laps around London, I was able to at least stay on the lead lap, although I finished over 10 minutes behind the leaders, almost getting lapped. That group had an intermediate sprint and that broke the group up pretty quickly. I rode that group ride mostly by myself.

      On another ride, four of us who got dropped formed a small group and we did a good job at staying together. We ended up finishing relatively low in the group rankings (at the end of an event, you will see how you fared). That was fun! I was riding with people from South Korea, Germany, and New Zealand. That, by itself, is pretty cool.

      A number of the bigger groups, especially the industry groups - Zwift Fitness, Team ODZ – tend to be more as advertised, that is to say, ride leaders tend to herd their cats… er… riders with success. Look, this is part of Zwift’s charm: These avatars aren’t some coder’s figment, they’re proxies for actual people.

      That’s enough dancing for now.

      We’ve talked extensively about the many ways that Zwift can motivate you, from individual achievements in our last article to today’s exploration of the Massively Multiplayer Online Game experience that differentiates Zwift from other indoor cycling tools. We focused primarily on group rides today. We’ve had our first dance, and hopefully you are looking to do this again soon.

      Slowtwitch offers 4 weekly events as of this writing, with more coming: 2 are Structured Training events (can't get dropped); 2 are group rides of all levels (can get dropped, but we have "sweeps" to keep the sprinter's bus company). We have some women's-only rides coming, and in general just more rides on the way. Go to our Reader Forum, you'll see the schedule there with links to the events, where you can join.

      There is still plenty more to cover in Zwift. We barely touched on races or group workouts today. Many Slowtwitchers are waiting for me to write about these. Next we’ll take a look at the aspects of Zwift that make it more than just a game, and how it can be used as a serious training tool. A seriously fun training tool!

      Here's the whole series:

      1. Welcome to Watopia: Install Zwift using virtual power, the most cost-effective option.
      2. Zwift, The Deeper Dive: One more layer below the surface.
      3. It's Game On With Zwift: Zwift is a game, but also a virtual community.
      4. Zwift: Let's Dance! The social aspect; Zwift is a Massive Multiplayer Game.
      5. Zwift: A Potent Tool for Training: Zwift’s workout mode.
      6. On Zwift Every Day is Race Day: Zwift's racing culture and community.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Zwift_Meetups_So_Happy_Together__7520.html b/site/slowtwitch.com/www/Indoor_Training/Zwift_Meetups_So_Happy_Together__7520.html new file mode 100644 index 0000000..703136c --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Zwift_Meetups_So_Happy_Together__7520.html @@ -0,0 +1,655 @@ + + + + +Zwift Meetups: So Happy Together! - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Zwift Meetups: So Happy Together!

      + +
      +
      +
      +

      There are many ways to motivate people, and indoor training software applications employ a wide array of methods to appeal to a wide range of personalities. Whether you’re the type of person who is intrinsically motivated by patiently and diligently working towards a goal that might not happen for years, the type of person who is more extrinsically motivated by enjoying a workout with friends, or somewhere in between, the plethora of indoor training options available to us can provide something for any of us.

      One aspect of training and fitness that many athletes enjoy is the social bond they enjoy with other athletes - people gathering together and bonding through a common interest. There is a lot of power in that basic social element of human nature: To be supported and to support others when we are at our best - or at our worst. To motivate and push one another to higher achievements. To celebrate our successes and to learn from our failures. To make friends or to enjoy friendly rivalries. These are some of the beautiful aspects of sport.

      Technology - and the Internet in particular - has provided us with a means to connect with others who share common interests, anywhere in the world. In theory, it’s a beautiful concept. In practice, it still lacks some aspects of social interaction that, as humans, we need to thrive.

      Indoor training is one of those areas where we’re continuing to see more and better ways to connect with other athletes. We have a lot of choices in how we can train indoors, and many of these have developed as a means to satisfy our needs replicate what we do outdoors - spin classes at your local gym, an Olympic triathlete training plan in TrainerRoad, indoor triathlons, races in Rouvy, group runs in Zwift, etc.

      The Social Side of Zwift

      So, let’s talk about how Zwift feeds our social needs. There is no doubt that Zwift is a very social indoor training platform. Zwift has been offering group rides, group workouts, and races for quite some time now. On any given day, you can head over to zwift.com/events or the Zwift Companion app and find a ride or race, probably within a couple of hours.

      But what if you want to organize your own event? Maybe you’re looking to take your weekly group ride to Watopia? Maybe you want to do a run with a friend? How about a coach who is looking for a way to keep a client engaged? This wasn’t so easy in Zwift until recently. If you wanted to organize an event in Zwift, you had to get in line. But, Zwift responded with a neat option - Meetups.

      We touched briefly on Meetups in our introduction to the Zwift Companion app a couple of weeks ago. We promised we would talk about this feature a bit more in the near future, so let’s dive in!

      +

      Calling all Social Directors

      This is your time to shine as your friendly (virtual) neighborhood Social Director. In order to create a Zwift Meetup, you need to get a few pieces in place:

      +

      Install the Zwift Companion App

      All of your Meetup management is going to occur through the Companion app. You can read up on how to get started with the Companion app here.

      +

      Navigate to Events

      Once you’ve logged into the Companion app, select the menu in the upper left corner, and select the Events menu option.

      +

      Navigate to Meetups

      On the Events screen, just below where it says “Events”, you’ll see 3 icons representing cycling events, running events, and Meetups. Select the Meetup icon.

      +

      View your Meetup history

      On the Meetup screen, if you’ve done any Meetups before, you’ll see them listed here. If not, you’ll see a bright orange “CREATE MEETUP” button just below the 3 event icons. Let’s select that button.

      +

      Create Your Meetup

      On the “Create Meetup” screen, you have the ability to set up your parameters for your meetup. You have several pieces to set up:
      • What do you want to do? This is the type of activity. Is your Meetup going to be a ride or a run? Select what you’re going to do here.

      • When do you want to meet? Select the date and time you want to meet up. Note that this will be based on your local time.

      • Where do you want to go? Depending on the date and time you selected for your Meetup, you’ll be able to choose whichever worlds are active at that time. Watopia is always an option, and the other world will depend on the world schedule. For example, since I chose December 7 in the screenshot above, London is the selected world for that day. Selecting the world will filter the routes accordingly. When you select a route, the route map will be displayed along with your choice of how you want to plan the duration for the event - do you want to ride/run for a certain amount of time (i.e, 30 minutes, 1 hour, etc.), or do you want to go a certain distance (i.e, 5 kilometers, 13.1 miles, 40 kilometers, 100 miles, etc.). This seems to be pretty open - if you want to schedule a 2000 mile group run on the Volcano Circuit, go for it!

      • Who do you want to invite? As it implies, now you get to select who gets invited to your prestigious event. In order to be able to invite people, you need to follow them, and they also need to follow you. So, you might have to do a bit of offline work on that - call up your friends, tell them to follow you on Zwift, and prepare to ride.

      • Customize your Meetup (cycling only). If you are doing a cycling Meetup, you have the option to keep everybody together during the ride. This is the Zwift equivalent of a “no drop” group ride, and a great option for those who might not feel like they are strong enough to keep up.

      • Message your group This optional message will be displayed in the Meetup invite and again as the first chat message when your Meetup is about to start.

      • Send! And with the click of the Send button, your Meetup is set! Meetups won’t show up in the public event calendar, and will only show up for those who you invited.

      • +

        During the Meetup

        Once the big day arrives, joining your Meetup is pretty easy - if you’re the organizer, you just log in to Zwift and get ready to ride. You can ride around in Zwift while you wait, and a few minutes before the scheduled Meetup time, you’ll be transported to the start, where you’ll be parked at the side of the road with your friends. When the clock hits the start time, you’ll all take off together. If you are the organizer, you’ll see a yellow chevron hanging over you as you ride or run.

        +

        The dynamics of the Meetup are pretty simple from here. On the social side, you’ll have the option to send messages to only the people in your Meetup. This is a fun way to chat, talk smack, or make excuses. If you’ve enabled the “Keep Everyone Together” option for a cycling Meetup, you’ll be kept together in a relatively tidy group. Now, if you’re really competitive, you might not like this - the overall speed and distance traveled by the group will reflect the average of the group. It does seem to create a bit of a yo-yo effect, depending on how close or far the riders are. If you have a wide range of cycling abilities represented in the group, it can get a bit strange. It might not give you the precise metrics of a solo ride, but you’re not doing Meetups for the numbers - this is social time! It’s fun!

        +

        Now Get Your Friends Together and Ride!

        There you have it: an introduction to Zwift Meetups, just one more way where Zwift encourages fun social interaction to motivate you! This is a great time of year to play with this, as after the holidays, it’s going to be time to get serious. In the mean time, gather your friends up and have fun!

        +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_Innsbruck_7534.html b/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_Innsbruck_7534.html new file mode 100644 index 0000000..95f2158 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_Innsbruck_7534.html @@ -0,0 +1,669 @@ + + + + +Zwift Route Maps: Innsbruck - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Zwift Route Maps: Innsbruck

      + +
      +
      +
      +

      Updated 5/10/2020 to improve the route data table and provide additional commentary on each route.

      Zwift includes a wide variety of routes in several worlds, and this gallery provides a collection of maps for the different routes in the Innsbruck world. Innsbruck, Austria, is nestled in the Alps and played host to the 2018 UCI World Championships. If you like to climb, there are plenty of climbing opportunities here, with very little in the form of flat routes.

      The following table and gallery provide more details on each route. For each map listed in the table below, we include the average grade to help you estimate the difficulty of the route. Most of the routes include a lead-in. The lead-in is the ride to the beginning of the circuit, so don't be surprised when it takes a bit longer than you thought it would to cover the expected distance.

      Once you complete a route, you will get an Achievement Badge for the route and earn bonus Experience Points (XP), in addition to the XP you earn for completing the distance of the route.

      Innsbruck Routes




      Route

      Length
      (miles / km)
      Elevation
      Gain
      (feet / m)

      Average
      Grade

      Bonus
      XP
      2018 UCI Worlds Short Lap14.7 / 23.71620 / 4942.09%470
      Achterbahn29.5 / 47.43244 / 9892.08%950
      Innsbruckring5.5 / 8.8252 / 770.87%%170
      Lutscher8.5 / 13.71315 / 4012.93%270
      Lutscher CCW8.5 / 13.71315 / 4012.93%240


      Innsbruck World Hack

      If you want to ride Innsbruck whenever you like, there is a hack that is described in the main article in this series. For Innsbruck, use world code 5:

      <WORLD>5</WORLD>

      Innsbruck Maps

      +

      + +

      + +

      2018 UCI Worlds Short Lap

      Length: 14.7 miles / 23.7 km
      Elevation Gain: 1620 feet / 494 m
      Average Grade: 2.09%
      Bonus XP (Experience Points): 470

      This route starts out relatively flat, and then takes you up the long climb to Igls, before coming back down to finish out on the relatively flat portion through downtown Innsbruck.

      +

      + +

      +

      Achterbahn

      Length: 29.5 miles / 47.4 km
      Elevation Gain: 3244 feet / 989 m
      Average Grade: 2.08%
      Bonus XP (Experience Points): 950

      The longest route in Innsbruck, you'll get to climb up to Igls twice - once from each direction.

      +

      + +

      +

      Innsbruckring

      Length: 5.5 miles / 8.8 km
      Elevation Gain: 252 feet / 77 m
      Average Grade: 0.87%
      Bonus XP (Experience Points): 170

      If you're looking for something a bit more flat in Innsbruck, this is your route. At 5.5 miles / 8.8 km, it's the shortest Innsbruck route and covers the flat circuit through downtown Innsbruck.

      +

      + +

      +

      Lutscher

      Length: 8.5 miles / 13.7 km
      Elevation Gain: 1315 feet / 401 m
      Average Grade: 2.93%
      Bonus XP (Experience Points): 270

      This route might catch you off guard. There is a big lead-in for this route - you start in downtown Innsbruck, climb to Igls, and then the route actually starts once you go through the banner at the top of the KOM. You'll cruise down the back side of the Igls climb and then do the KOM a second time to actually complete the route. Be ready for this one!

      +

      + +

      +

      Lutscher CCW

      Length: 8.5 miles / 13.8 km
      Elevation Gain: 1315 feet / 401 m
      Average Grade: 2.93%
      Bonus XP (Experience Points): 240

      Like the Lutscher route, this one also has a substantial lead-in that will require you to do the reverse KOM leading up to the start of the route at the KOM banner. You'll then need to make the climb a second time to complete the route. This is a hard-earned 240 XP!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_London_7535.html b/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_London_7535.html new file mode 100644 index 0000000..c99f610 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_London_7535.html @@ -0,0 +1,701 @@ + + + + +Zwift Route Maps: London - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Zwift Route Maps: London

      + +
      +
      +
      +

      Updated 5/5/2020 to improve the route data table and provide a bit more information on each route.

      Zwift includes a wide variety of routes in several worlds, and this gallery provides a collection of maps for the different routes in the London world.

      The following table and gallery provide more details on each route. Distances shown are as published in Zwift, but be aware that many of these rides have a lead-in. The lead-in is the ride to the beginning of the circuit, so you're not surprised when it takes a bit longer than you thought it would to cover the expected distance.

      Once you complete a route, you will get an Achievement Badge for the route and earn bonus Experience Points (XP), in addition to the XP you earn for covering the distance of the route.

      London Routes



      Route

      Length
      (miles / km)
      Elevation
      Gain
      (feet / m)

      Average
      Grade

      Bonus
      XP
      Classique3.4 / 5.482 / 250.46%110
      Greater London 814.8 / 23.8902 / 2751.15%480
      Greater London Flat7.3 / 11.7173 / 530.45%230
      Greater London Loop13.1 / 21.0836 / 2551.21%420
      Greatest London Flat14.7 / 23.6534 / 1630.69%500
      Greatest London Loop15.9 / 25.71164 / 3551.39%510
      London 812.6 / 20.3836 / 2551.26%410
      London Loop9.2 / 14.9754 / 2301.55%300
      Surrey Hills24.3 / 39.12877 / 8772.24%820
      The London Pretzel34.6 / 55.71876 / 5721.03%1100
      The PRL Full107.7 / 173.38605 / 26231.51%3460
      The PRL Half43.0 / 69.23310 / 10091.46%1380
      Triple Loops25.4 / 40.91850 / 5641.38%810


      London World Hack

      If you want to ride London whenever you like, there is a hack that is described in the main article in this series. For London, use world code 3:

      <WORLD>3</WORLD>

      London Maps

      +

      + +

      + +

      Classique

      Length: 3.4 miles / 5.4 km
      Elevation Gain: 82 feet / 25 m
      Average Grade: 0.46%
      Bonus XP (Experience Points): 110

      This is a short, flat, and fun introduction to London, but don't get too thrown off when you reach the expected distance but don't earn the badge - this has a lead-in (shown in blue in the image above) that is actually longer than the course itself!

      +

      + +

      +

      Greater London 8

      Length: 14.8 miles / 23.8 km
      Elevation Gain: 902 feet / 275 m
      Average Grade: 1.15%
      Bonus XP (Experience Points): 480

      This route would be relatively flat, if it weren't for the inclusion of the infamous Box Hill.

      +

      + +

      +

      Greater London Flat

      Length: 7.3 miles / 11.7 km
      Elevation Gain: 173 feet / 53 m
      Average Grade: 0.45%
      Bonus XP (Experience Points): 230

      As the name implies, this is the basically tied with the Classique as the flattest London route, with only 173 feet of elevation gain over 7.3 miles. Like the Classique, this one has a longer lead-in than you might expect, so plan for a longer ride than what Zwift tells you.

      +

      + +

      +

      Greater London Loop

      Length: 13.1 miles / 21.0 km
      Elevation Gain: 836 feet / 255 m
      Average Grade: 1.21%
      Bonus XP (Experience Points): 420

      With a minimal lead-in, this route takes you on a figure 8 through London and then up Box Hill.

      +

      + +

      +

      Greatest London Flat

      Length: 14.7 miles / 23.6 km
      Elevation Gain: 534 feet / 163 m
      Average Grade: 0.69%
      Bonus XP (Experience Points): 500

      If you want to keep it flat but get some miles in, this is the longest flat London route.

      +

      + +

      +

      Greatest London Loop

      Length: 15.9 miles / 25.7 km
      Elevation Gain: 1164 feet / 355 m
      Average Grade: 1.39%
      Bonus XP (Experience Points): 510

      This is a great route to cover a lot of the London world in one trip. You'll bypass Box Hill in favor of Leith Hill.

      +

      + +

      +

      London 8

      Length: 12.6 miles / 20.3 km
      Elevation Gain: 836 feet / 255 m
      Average Grade: 1.26%
      Bonus XP (Experience Points): 410

      A relatively moderate figure 8 route allowing you to experience London and Box Hill.

      +

      + +

      +

      London Loop

      Length: 9.2 miles / 14.9 km
      Elevation Gain: 754 feet / 230 m
      Average Grade: 1.55%
      Bonus XP (Experience Points): 300

      A nice ride along the Thames that bypasses the Classique course in favor of heading into the English hillside.

      +

      + +

      +

      Surrey Hills

      Length: 24.3 miles / 39.1 km
      Elevation Gain: 2877 feet / 877 m
      Average Grade: 2.24%
      Bonus XP (Experience Points): 820

      If you want to get your climb on in London, this is the way to do it. With the highest average grade of any of the London routes, you'll cover the Surrey passes in both directions. This route has a short lead-in, so plan accordingly.

      +

      + +

      +

      The London Pretzel

      Length: 34.6 miles / 55.7 km
      Elevation Gain: 1876 feet / 572 m
      Average Grade: 1.03%
      Bonus XP (Experience Points): 1100

      The third-longest official London route, The London Pretzel will allow you to rack up a pile of XP if you're looking to level up - you'll get as much as 1100 XP for the route distance (if you use metric), plus an 1100 XP bonus for completing the route.

      +

      + +

      +

      The PRL Full

      Length: 107.7 miles / 173.3 km
      Elevation Gain: 8605 feet / 2623 m
      Average Grade: 1.51%
      Bonus XP (Experience Points): 3460

      This is the longest route in all of Zwift, with plenty of climbing - matter of fact, you're going to actually do the same route as the London Loop, but you're going to do it 11 times. Are you ready to climb Box Hill 11 times?

      +

      + +

      +

      The PRL Half

      Length: 43.0 miles / 69.2 km
      Elevation Gain: 3310 feet / 1009 m
      Average Grade: 1.46%
      Bonus XP (Experience Points): 1380

      Like its big sibling, the PRL Half follows the London Loop, but instead of 11 circuits, you'll do 4. Piece of cake, right?

      +

      + +

      +

      Triple Loops

      Length: 25.4 miles / 40.9 km
      Elevation Gain: 1850 feet / 564 m
      Average Grade: 1.38%
      Bonus XP (Experience Points): 810

      This route allows you to experience just about every mile of the London World, including London, Box Hill, and Leith Hill.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_New_York_7536.html b/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_New_York_7536.html new file mode 100644 index 0000000..ec3c355 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_New_York_7536.html @@ -0,0 +1,693 @@ + + + + +Zwift Route Maps: New York - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Zwift Route Maps: New York

      + +
      +
      +
      +

      Updated 5/8/2020 to improve the route data table and provide a bit more information on each route.

      Zwift includes a wide variety of routes in several worlds, and this gallery provides a collection of maps for the different routes in the New York world.

      Zwift's vision of New York is a futuristic one - the routes are centered around Central Park, but include high routes for more mileage and a unique view of a New York with futuristic skyscrapers and flying cars.

      The following table and gallery provide more details on each route. The distances and elevation gain provided are as listed by Zwift, but some of these routes have a substantial lead-in. The lead-in is the ride to the beginning of the circuit, so don't be surprised when it takes a bit longer than you thought it would to cover the expected distance.

      Once you complete a route, you will get an Achievement Badge for the route and earn bonus Experience Points (XP), in addition to the XP you earn for covering the distance of the route.

      New York Routes



      Route

      Length
      (miles / km)
      Elevation
      Gain
      (feet / m)

      Average
      Grade

      Bonus
      XP
      Astoria Line 87.2 / 11.6462 / 1411.22%230
      Everything Bagel21.3 / 34.31784 / 5441.59%690
      Grand Central Circuit4.3 / 6.9472 / 1442.08%140
      Knickerbocker14.0 / 22.51194 / 3641.62%450
      Knickerbocker Reverse14.0 / 22.51194 / 3641.62%450
      Lady Liberty7.7 / 12.4675 / 2061.66%240
      Mighty Metropolitan12.5 / 20.11043 / 3181.58%400
      Park Perimeter Loop6.1 / 9.8413 / 1261.28%190
      Rising Empire12.9 / 20.81233 / 3761.81%410
      The 6 Train4.0 / 6.5226 / 691.07%130
      The Highline6.5 / 10.5583 / 1781.70%210


      New York World Hack

      If you want to ride New York whenever you like, there is a hack that is described in the main article in this series. For New York, use world code 4:

      <WORLD>4</WORLD>

      New York Maps

      +

      + +

      + +

      Astoria Line 8

      Length: 7.2 miles / 11.6 km
      Elevation Gain: 462 feet / 141 m
      Average Grade: 1.22%
      Bonus XP (Experience Points): 230

      Zwift's version of Central Park has some short, punchy climbs to mix things up a bit, but for the most part, this figure 8 route is pretty flat.

      +

      + +

      +

      Everything Bagel

      Length: 21.3 miles / 34.3 km
      Elevation Gain: 1784 feet / 544 m
      Average Grade: 1.59%
      Bonus XP (Experience Points): 690

      Everything Bagel is currently New York's big dog - it is the longest route with the most elevation gain, and you'll hit the KOM and sprint from both directions.

      +

      + +

      +

      Grand Central Circuit

      Length: 4.3 miles / 6.9 km
      Elevation Gain: 472 feet / 144 m
      Average Grade: 2.08%
      Bonus XP (Experience Points): 140

      If you want to get some climbing in over a short distance, this is your route. You'll spend most of your time on the New York KOM. There is a very short but flat lead-in, so give yourself just a little extra time.

      +

      + +

      +

      Knickerbocker

      Length: 14.0 miles / 22.5 km
      Elevation Gain: 1194 feet / 364 m
      Average Grade: 1.62%
      Bonus XP (Experience Points): 450

      Overall, this route is pretty flat, aside from hitting the New York KOM just short of halfway into the route.

      +

      + +

      +

      Knickerbocker Reverse

      Length: 14.0 miles / 22.5 km
      Elevation Gain: 1194 feet / 364 m
      Average Grade: 1.62%
      Bonus XP (Experience Points): 450

      As the name implies, this is the Knickerbocker route, but pointed in the other direction. Overall, this route is pretty flat, aside from hitting the New York KOM just past halfway into the route.

      +

      + +

      +

      Lady Liberty

      Length: 7.7 miles / 12.4 km
      Elevation Gain: 675 feet / 206 m
      Average Grade: 1.66%
      Bonus XP (Experience Points): 240

      This route doesn't take you to the Statue of Liberty, but you will climb over twice her height on this 7.7 mile route up the New York KOM before a lap around Central Park.

      +

      + +

      +

      Mighty Metropolitan

      Length: 12.5 miles / 20.1 km
      Elevation Gain: 1043 feet / 318 m
      Average Grade: 1.58%
      Bonus XP (Experience Points): 400

      You'll cover most of the New York roads in this route, starting with a gentle start in Central Park before hitting the KOM about half way through the ride. After cruising down the backside of the KOM, you'll go back to the more gentle terrain of Central Park.


      +

      + +

      +

      Park Perimeter Loop

      Length: 6.1 miles / 9.8 km
      Elevation Gain: 413 feet / 126 m
      Average Grade: 1.28%
      Bonus XP (Experience Points) Earned: 190

      You'll enjoy this mostly flat tour of the outer loop of Central Park. As far as the New York routes go, this one is relatively gentle.


      +

      + +

      +

      Rising Empire

      Length: 12.9 miles / 20.8 km
      Elevation Gain: 1233 feet / 376 m
      Average Grade: 1.81%
      Bonus XP (Experience Points): 410

      The second longest New York route, Rising Empire puts emphasis on the "Rising" by having you do the KOM from both directions. This is a fun one for those who like to climb!

      +

      + +

      +

      The 6 Train

      Length: 4.0 miles / 6.5 km
      Elevation Gain: 226 feet / 69 m
      Average Grade: 1.07%
      Bonus XP (Experience Points): 130

      The 6 Train is short and relatively flat, with a couple short climbs all at ground level within Central Park. After a .4 mile mostly downhill lead-in, you'll be treated to a mostly flat ride with one short 10% grade climb and a couple of other more gentle, short climbs. This is a fun one if you want to complete the route but only have 20-30 minutes to ride.

      +

      + +

      +

      The Highline

      Length: 6.5 miles / 10.5 km
      Elevation Gain: 583 feet / 178 m
      Average Grade: 1.70%
      Bonus XP (Experience Points): 210

      The Highline might surprise you a bit - the lead-in to the circuit is pretty brutal, as it will take you up the New York KOM before you actually start the Highline route, and then the New York KOM serves as the end of the Highline route. So, be ready to do the New York KOM twice and an actual total distance of 9.7 miles with 1008 feet of elevation gain!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_Richmond_7537.html b/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_Richmond_7537.html new file mode 100644 index 0000000..679c73e --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_Richmond_7537.html @@ -0,0 +1,661 @@ + + + + +Zwift Route Maps: Richmond - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Zwift Route Maps: Richmond

      + +
      +
      +
      +

      Updated 5/7/2020 to improve the route information table and provide updated information on each route.

      Zwift includes a wide variety of routes in several worlds, and this gallery provides a collection of maps for the different routes in the Richmond world.

      Modeled after the real city of Richmond, Virginia, USA, which hosted the 2015 UCI Worlds, Richmond was the first Zwift world outside of Watopia. In February 2020, Richmond got an extensive facelift, including new names for the routes. Richmond includes 3 routes, all based on the 2015 UCI Worlds course.

      The following table and gallery provide more details on each route. Many of the routes in Zwift include a lead-in. The lead-in is the ride to the beginning of the circuit, so don't be surprised when it takes a bit longer than you thought it would to cover the expected distance.

      Once you complete a route, you will get an Achievement Badge for the route and earn bonus Experience Points (XP), in addition to the XP you earn for covering the distance of the route. Earn your route badge and the next level up by finishing each route!

      Richmond Routes



      Route

      Length
      (miles / km)
      Elevation
      Gain
      (feet / m)

      Average
      Grade

      Bonus
      XP
      2015 UCI Worlds Course10.1 / 16.2515 / 1570.97%320
      Cobbled Climbs5.7 / 9.2436 / 1331.45%180
      The Fan Flats3.1 / 5.162 / 190.38%100


      Richmond World Hack

      If you want to ride Richmond whenever you like, there is a hack that is described in the main article in this series. For Richmond, use world code 2:

      <WORLD>2</WORLD>

      Richmond Maps

      +

      + +

      + +

      2015 UCI Worlds Course

      Length: 10.1 miles / 16.2 km
      Elevation Gain: 515 feet / 157 m
      Average Grade: 0.97%
      Bonus XP (Experience Points): 320

      Modeled after the 2015 UCI Worlds course, this route takes you through the heart of Richmond, Virginia.

      +

      + +

      +

      The Fan Flats

      Length: 3.1 miles / 5.1 km
      Elevation Gain: 62 feet / 19 m
      Average Grade: 0.38%
      Bonus XP (Experience Points): 100

      Formerly known as "Prefer Flat Roads", this route covers the flatter portion of the Richmond 2015 UCI Worlds course.

      +

      + +

      +

      Cobbled Climbs

      Length: 5.7 miles / 9.2 km
      Elevation Gain: 436 feet / 133 m
      Average Grade: 1.45%
      Bonus XP (Experience Points): 180

      This route, formerly known as "Prefer Hilly Roads", covers the hillier portion of the 2015 UCI Worlds course.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_Watopia_Part_1__7539.html b/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_Watopia_Part_1__7539.html new file mode 100644 index 0000000..4ec6517 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_Watopia_Part_1__7539.html @@ -0,0 +1,709 @@ + + + + +Zwift Route Maps: Watopia (Part 1) - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Zwift Route Maps: Watopia (Part 1)

      + +
      +
      +
      +

      Updated 5/8/2020 to improve the route data table and provide additional commentary on each route.

      Zwift includes a wide variety of routes in several worlds, and this gallery provides a collection of maps for the different routes in the Watopia world. Because there are so many routes in Watopia, we’ve broken the routes up into two galleries, listed alphabetically by route name. You can find Watopia (Part 2) here.

      The following table and gallery provide more details on each route. For each map listed in the table below, we include the average grade of the entire route to help you gauge the difficulty. Some of these routes include a substantial lead-in. The lead-in is the ride to the beginning of the circuit, so don't be surprised when it takes a bit longer than you thought it would to cover the expected distance.

      Once you complete a route, you will get an Achievement Badge for the route and earn bonus Experience Points (XP), in addition to the XP you earn for completing the distance of the route.

      Watopia Routes (Part 1: A-Q)



      Route

      Length
      (miles / km)
      Elevation
      Gain
      (feet / m)

      Average
      Grade

      Bonus
      XP
      Big Foot Hills42.0 / 67.62319 / 7071.05%1340
      Big Loop26.5 / 42.62168 / 6611.55%840
      Bigger Loop33.1 / 53.22267 / 6911.30%1060
      Dust In The Wind32.4 / 52.11909 / 5821.12%1080
      Figure 818.5 / 29.7833 / 2540.85%580
      Figure 8 Reverse18.5 / 29.7833 / 2540.85%580
      Flat Route6.4 / 10.3200 / 610.59%200
      Four Horsemen55.8 / 89.86916 / 21082.35%1780
      Hilly Route5.7 / 9.2354 / 1081.18%180
      Jungle Circuit4.9 / 7.9269 / 821.04%260
      Mountain 820.0 / 32.32267 / 6912.15%640
      Mountain Route18.5 / 29.72237 / 6822.29%580
      Muir And The Mountain21.2 / 34.12598 / 7922.32%680
      Out And Back Again24.8 / 39.91072 / 3270.82%840
      Quatch Quest28.9 / 46.55597 / 17063.67%920
      Click here to view the other half of the Watopia routes.

      Watopia World Hack

      Watopia is always available for riding, so there isn’t any need to hack it in order to ride any of these routes.

      Watopia Maps (Part 1: A-Q)

      +

      + +

      + +

      Big Foot Hills

      Length: 42.0 miles / 67.6 km
      Elevation Gain: 2319 feet / 707 m
      Average Grade: 1.05%
      Bonus XP (Experience Points): 1340



      +

      + +

      +

      Big Loop

      Length: 26.5 miles / 42.6 km
      Elevation Gain: 2168 feet / 661 m
      Average Grade: 1.55%
      Bonus XP (Experience Points): 840



      +

      + +

      +

      Bigger Loop

      Length: 33.1 miles / 53.2 km
      Elevation Gain: 2267 feet / 691 m
      Average Grade: 1.30%
      Bonus XP (Experience Points): 1060



      +

      + +

      +

      Dust In The Wind

      Length: 32.4 miles / 52.1 km
      Elevation Gain: 1909 feet / 582 m
      Average Grade: 1.12%
      Bonus XP (Experience Points): 1080



      +

      + +

      +

      Figure 8

      Length: 18.5 miles / 29.7 km
      Elevation Gain: 833 feet / 254 m
      Average Grade: 0.85%
      Bonus XP (Experience Points): 580



      +

      + +

      +

      Figure 8 Reverse

      Length: 18.5 miles / 29.7 km
      Elevation Gain: 833 feet / 254 m
      Average Grade: 0.85%
      Bonus XP (Experience Points): 580



      +

      + +

      +

      Flat Route

      Length: 6.4 miles / 10.3 km
      Elevation Gain: 200 feet / 61 m
      Average Grade: 0.59%
      Bonus XP (Experience Points): 200



      +

      + +

      +

      Four Horsemen

      Length: 55.8 miles / 89.8 km
      Elevation Gain: 6916 feet / 2108 m
      Average Grade: 2.35%
      Bonus XP (Experience Points): 1780



      +

      + +

      +

      Hilly Route

      Length: 5.7 miles / 9.2 km
      Elevation Gain: 354 feet / 108 m
      Average Grade: 1.18%
      Bonus XP (Experience Points): 180



      +

      + +

      +

      Jungle Circuit

      Length: 4.9 miles / 7.9 miles
      Elevation Gain: 269 feet / 82 m
      Average Grade: 1.04%
      Bonus XP (Experience Points): 260



      +

      + +

      +

      Mountain 8

      Length: 20.0 miles / 32.3 km
      Elevation Gain: 2267 feet / 691 m
      Average Grade: 2.15%
      Bonus XP (Experience Points): 640



      +

      + +

      +

      Mountain Route

      Length: 18.5 miles / 29.7 km
      Elevation Gain: 2237 feet / 682 m
      Average Grade: 2.29%
      Bonus XP (Experience Points): 580



      +

      + +

      +

      Muir And The Mountain

      Length: 21.2 miles / 34.1 km
      Elevation Gain: 2598 feet / 792 m
      Average Grade: 2.32%
      Bonus XP (Experience Points): 680



      +

      + +

      +

      Out And Back Again

      Length: 24.8 miles / 39.9 km
      Elevation Gain: 1072 feet / 327 m
      Average Grade: 0.82%
      Bonus XP (Experience Points): 840



      +

      + +

      +

      Quatch Quest

      Length: 28.9 miles / 46.5 km
      Elevation Gain: 5597 feet / 1706 m
      Average Grade: 3.67%
      Bonus XP (Experience Points): 920




      If you would like to see the rest of the Watopia routes, check out Watopia Part 2 at this link.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_Watopia_Part_2__7540.html b/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_Watopia_Part_2__7540.html new file mode 100644 index 0000000..3310157 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_Watopia_Part_2__7540.html @@ -0,0 +1,709 @@ + + + + +Zwift Route Maps: Watopia (Part 2) - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Zwift Route Maps: Watopia (Part 2)

      + +
      +
      +
      +

      Updated 5/9/2020 to improve the route data table and provide additional commentary on each route.

      Zwift includes a wide variety of routes in several worlds, and this gallery provides a collection of maps for the different routes in the Watopia world. Because there are so many routes in Watopia, we’ve broken the routes up into two galleries, listed alphabetically by route name. You can find Watopia (Part 1) here.

      The following table and gallery provide more details on each route. For each map listed in the table below, we include the average grade to help you estimate the difficulty of the route. Most of the routes include a lead-in. The lead-in is the ride to the beginning of the circuit, so don't be surprised when it takes a bit longer than you thought it would to cover the expected distance.

      Once you complete a route, you will get an Achievement Badge for the route and earn bonus Experience Points (XP), in addition to the XP you earn for completing the distance of the route.

      Watopia Routes (Part 2: R-Z)



      Route

      Length
      (miles / km)
      Elevation
      Gain
      (feet / m)

      Average
      Grade

      Bonus
      XP
      Road To Ruins18.4 / 29.6898 / 2740.92%580
      Road To Sky10.9 / 17.53428 / 10455.96%380
      Sand And Sequoias12.5 / 20.2570 / 1740.86%400
      Tempus Fugit10.7 / 17.282 / 250.15%380
      The Mega Pretzel66.7 / 107.35374 / 16381.53%2140
      The Pretzel45.1 / 72.54461 / 13601.87%1440
      The Uber Pretzel79.7 / 128.37805 / 23791.85%2560
      Three Sisters29.8 / 48.02936 / 8951.87%950
      Tick Tock10.5 / 16.9173 / 530.31%380
      Tour Of Fire And Ice15.8 / 25.43818 / 11644.58%500
      Volcano Circuit2.5 / 4.165 / 200.49%80
      Volcano Circuit CCW2.5 / 4.165 / 200.49%80
      Volcano Climb14.2 / 22.9666 / 2030.89%460
      Volcano Flat7.7 / 12.4160 / 490.39%240
      Whole Lotta Lava7.6 / 12.3524 / 1601.31%240
      Click here to view the other half of the Watopia routes.

      Watopia World Hack

      Watopia is always available for riding, so there isn’t any need to hack it in order to ride any of these routes.

      Watopia Maps (Part 2: R-Z)

      +

      + +

      + +

      Road To Ruins

      Length: 18.4 miles / 29.6 km
      Elevation Gain: 898 feet / 274 m
      Average Grade: 0.92%
      Bonus XP (Experience Points): 580



      +

      + +

      +

      Road To Sky

      Length: 10.9 miles / 17.5 km
      Elevation Gain: 3428 feet / 1045 m
      Average Grade: 5.96%
      Bonus XP (Experience Points): 380



      +

      + +

      +

      Sand And Sequoias

      Length: 12.5 miles / 20.2 km
      Elevation Gain: 570 feet / 174 m
      Average Grade: 0.86%
      Bonus XP (Experience Points): 400



      +

      + +

      +

      Tempus Fugit

      Length: 10.7 miles / 17.2 km
      Elevation Gain: 82 feet / 25 m
      Average Grade: 0.15%
      Bonus XP (Experience Points): 380



      +

      + +

      +

      The Mega Pretzel

      Length: 66.7 miles / 107.3 km
      Elevation Gain: 5374 feet / 1638 m
      Average Grade: 1.53%
      Bonus XP (Experience Points): 2140



      +

      + +

      +

      The Pretzel

      Length: 45.1 miles / 72.5 km
      Elevation Gain: 4461 feet / 1360 m
      Average Grade: 1.87%
      Bonus XP (Experience Points): 1440



      +

      + +

      +

      The Uber Pretzel

      Length: 79.7 miles / 128.3 km
      Elevation Gain: 7805 feet / 2379 m
      Average Grade: 1.85%
      Bonus XP (Experience Points): 2560



      +

      + +

      +

      Three Sisters

      Length: 29.8 miles / 48.0 km
      Elevation Gain: 2936 feet / 895 m
      Average Grade: 1.87%
      Bonus XP (Experience Points): 950



      +

      + +

      +

      Tick Tock

      Length: 10.5 miles / 16.9 km
      Elevation Gain: 173 feet / 53 m
      Average Grade: 0.31%
      Bonus XP (Experience Points): 380



      +

      + +

      +

      Tour Of Fire And Ice

      Length: 15.8 miles / 25.4 km
      Elevation Gain: 3818 feet / 1164 m
      Average Grade: 4.58%
      Bonus XP (Experience Points): 500



      +

      + +

      +

      Volcano Circuit

      Length: 2.5 miles / 4.1 km
      Elevation Gain: 65 feet / 20 m
      Average Grade: 0.49%
      Bonus XP (Experience Points): 80



      +

      + +

      +

      Volcano Circuit CCW

      Length: 2.5 m / 4.1 km
      Elevation Gain: 65 feet / 20 m
      Average Grade: 0.49%
      Bonus XP (Experience Points): 80



      +

      + +

      +

      Volcano Climb

      Length: 14.2 miles / 22.9 km
      Elevation Gain: 666 feet / 203 m
      Average Grade: 0.89%
      Bonus XP (Experience Points): 460



      +

      + +

      +

      Volcano Flat

      Length: 7.7 miles / 12.4 km
      Elevation Gain: 160 feet / 49 m
      Average Grade: 0.39%
      Bonus XP (Experience Points): 240

      The Volcano Flat route takes you through the classic areas of Watopia, staying on a relatively flat course through the ocean tunnel, the Italian Villas, and through part of the volcano before returning to the start. Other than a couple of gentle hills, this one is pretty much staying near sea level the entire time.

      +

      + +

      +

      Whole Lotta Lava

      Length: 7.6 miles / 12.3 km
      Elevation Gain: 524 feet / 160 m
      Average Grade: 1.31%
      Bonus XP (Experience Points): 240




      If you would like to see the rest of the Watopia routes, check out Watopia Part 1 at this link.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_Yorkshire_7541.html b/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_Yorkshire_7541.html new file mode 100644 index 0000000..1c54267 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Zwift_Route_Maps_Yorkshire_7541.html @@ -0,0 +1,669 @@ + + + + +Zwift Route Maps: Yorkshire - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Zwift Route Maps: Yorkshire

      + +
      +
      +
      +

      Updated 5/6/2020 to improve the route information table and provide additional information on each route.

      Zwift includes a wide variety of routes in several worlds, and this gallery provides a collection of maps for the different routes in the Yorkshire world.

      Yorkshire was added to Zwift in September 2019 and is based on the route used for the 2019 UCI Worlds in Harrogate, North Yorkshire, England. The UCI Worlds circuit has been turned into 5 routes ranging from 1.9 miles (3 km) to 17.2 miles (27.7 km) with some moderate climbing.

      The following table and gallery provide more details on each route. We’re in the process of riding all of these routes, so check back for updates. Most routes in Zwift have some sort of lead-in - the lead-in is the ride to the beginning of the circuit, so don't be surprised when it takes a bit longer than you thought it would to cover the expected distance.

      Once you complete a route, you will get an Achievement Badge for the route and earn bonus Experience Points (XP) in addition to the XP you earn for covering the distance of the route.

      Yorkshire Routes



      Route

      Length
      (miles / km)
      Elevation
      Gain
      (feet / m)

      Average
      Grade

      Bonus
      XP
      2019 UCI Worlds Harrogate Circuit8.6 / 13.8803 / 2451.77%270
      Duchy Estate1.9 / 3.0131 / 401.31%60
      Queen’s Highway1.9 / 3.0131 / 401.31%60
      Royal Pump Room 817.2 / 27.71607 / 4901.77%550
      Tour Of Tewit Well6.8 / 10.9669 / 2041.86%210


      Yorkshire World Hack

      If you want to ride Yorkshire whenever you like, there is a hack that is described in the main article in this series. For Yorkshire, use world code 7:

      <WORLD>7</WORLD>

      Yorkshire Maps

      +

      + +

      + +

      2019 UCI Worlds Harrogate Circuit

      Length: 8.6 miles / 13.8 km
      Elevation Gain: 803 feet / 245 m
      Average Grade: 1.77%
      Bonus XP (Experience Points): 270

      This route, as the name implies follows the 2019 UCI Worlds route. You'll start out on A61 just north of Tewit Well, turning east towards the Royal Pump room and make a short loop in the streets of Harrogate. You'll pass the Royal Pump room a second time and then head out into the Yorkshire countryside for a longer loop before returning to the start point.

      +

      + +

      +

      Duchy Estate

      Length: 1.9 miles / 3.0 km
      Elevation Gain: 131 feet / 40 m
      Average Grade: 1.31%
      Bonus XP (Experience Points) 60

      Duchy Estate makes use of the short northern part of the UCI Worlds circuit, staying within central Harrogate for the entire route. There is a short lead-in, and the route is relatively flat, as far as Yorkshire goes. Keep your eyes open for the Royal Pump Room and enjoy the sights of Millionaires' Row.

      +

      + +

      +

      Queen's Highway

      Length: 1.9 miles / 3.0 km
      Elevation Gain: 131 feet / 40 m
      Average Grade: 1.31%
      Bonus XP (Experience Points): 60

      The Queen's Highway route is based on the same portion of the Harrogate circuit, but in reverse. It has a slightly longer lead-in, but otherwise nets out to be the same distance, elevation gain, and average grade.

      +

      + +

      +

      Royal Pump Room 8

      Length: 17.2 miles / 27.7 km
      Elevation Gain: 1607 feet / 490 m
      Average Grade: 1.77%
      Bonus XP (Experience Points): 550

      The Royal Pump Room 8 utilizes the entire UCI Worlds circuit, but in a figure 8 configuration allowing you to cover the entire route in both directions, and effectively doubling the distance of the UCI Worlds circuit. This is currently the longest route in the Yorkshire world.

      +

      + +

      +

      Tour Of Tewit Well

      Length: 6.8 miles / 10.9 km
      Elevation Gain: 669 feet / 204 m
      Average Grade: 1.86%
      Bonus XP (Experience Points): 210

      This route makes use of the longer southern portion of the UCI Worlds route, bypassing the short northern part of the circuit in central Harrogate and getting you out into the Yorkshire countryside. If you're looking for Tewit Well, you'll come close to it towards the end of the route, but the route doesn't actually take you past it.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Zwift_Routes_Oh_the_Places_you_ll_Go__7542.html b/site/slowtwitch.com/www/Indoor_Training/Zwift_Routes_Oh_the_Places_you_ll_Go__7542.html new file mode 100644 index 0000000..26c7e76 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Zwift_Routes_Oh_the_Places_you_ll_Go__7542.html @@ -0,0 +1,650 @@ + + + + +Zwift Routes: Oh, the Places you'll Go! - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Zwift Routes: Oh, the Places you'll Go!

      + +
      +
      +
      +

      Updated 5/5/2020 to reflect updates to Guest World functionality and how you can get the Slowtwitch kit for your Zwift avatar.

      In December 2019, Zwift released the details of their newest world, Crit City. Crit City is currently an “event only” world, meaning that the only way you can ride it is if you sign up for an event on the calendar. You can see upcoming events at www.zwift.com/events or use the Zwift Companion App to search for events being hosted at Crit City.

      For those of us who might not be ready for, or interested in, indoor racing, we still have plenty of fun options to explore. We’ll be delving deeper into structured training and racing soon, but today we’re still focused on the “free ride” aspects of indoor training, looking at all of the different worlds and routes that are available for us to ride at our own pace.

      One of the fun aspects of a game-like application like Zwift is that you are not bound to the constraints of the real world. In their efforts to enhance our indoor training experience since the Zwift beta was launched in September of 2014, the Zwift team has been increasing the number of worlds and routes dramatically. Remember when it was just one route around Watopia, and you couldn’t even do U-turns? Zwift has come a long way since then, currently including a total of 6 worlds with 67 routes available for free riding, along with the Bologna TT course and Crit City as “event only” worlds, and several running routes that aren’t listed here yet. In an effort to explore all of our options, we’ve compiled a list of the different worlds and created galleries containing maps of all the routes in Zwift. You can start to explore, by Zwift world, here:

      Zwift Worlds


      World
      Map
      Gallery

      Routes
      InnsbruckMaps5
      LondonMaps13
      New YorkMaps11
      RichmondMaps3
      Watopia (Part 1)Maps15
      Watopia (Part 2)Maps15
      YorkshireMaps5

      Note that we broke Watopia up into 2 parts because there are so many routes. All things Zwift started with Watopia (technically, there was Jarvis before Watopia, but that was in the closed beta. You can still find a fun reference to Jarvis in Watopia, if you know where to look). Watopia is not based on our real world, which opens up a lot of opportunities for the creative minds at Zwift. Where else can you ride through the desert, a forest of giant sequoias, under the ocean, and through a volcano in the same day? For example, the Big Foot Hills route in Zwift will let you do all of that in just under 42 miles. Watopia gives the Zwift team a lot of creative license, and the 30 routes in Zwift demonstrate some of that creativity.

      On the other hand, Zwift also offers several worlds that are based on real places - Innsbruck, London, New York, Richmond, and Yorkshire. While based in reality, these worlds still offer some fun takes on these real-life locales, offering a creative take on many aspects of each world. For example, New York has gone futuristic, offering routes above the normal sights and sounds of Central Park. Just watch out for flying cars.

      +

      Route Achievements

      If you like to explore, or at least enjoy some variety by training in some different virtual surroundings, what better way to do it than to ride as many of the Zwift routes as possible? In order to provide a little motivation in that area, Zwift quietly introduced a few carrots for us at the same time they rolled out Crit City - there are now Route Achievements for every single one of the routes. Completing the entire route will earn you a badge, as well as Experience Points (XP) based on the length and difficulty of the route. So, if you like chasing those Zwift badges, the number has now gone up dramatically, with a total of 122 badges now available.

      For some of us, these route achievement badges are something fun to chase and provide some motivation over the winter months. With that said, the information provided for each route can be a bit deceiving. The galleries in the table above have all of the lengths and elevation gains for each route, as stated by Zwift. What Zwift doesn’t give us is the XP you’ll earn for completing the route (in addition to the normal XP you earn per kilometer/mile), nor does it account for the lead-in - the ride to the ride, if you will. For most Zwift routes, you will have a short ride to the start. It seems that can vary quite a bit, and you might need to account for it.

      For example, the Watopia Hilly Route drops you into Watopia just under 2 tenths of a mile from the start, and you only gain 4 feet of elevation before you are actually on the route. The Highline route in New York indicates a route length of 6.5 miles with 583 feet of elevation gain. But, the lead-in to the Highline is significant - by the time you actually complete the Highline, you’ll have covered 9.7 miles, climbed 1008 feet, and did the New York KOM twice. So, consider yourself warned - if you’ve got a certain amount of time budgeted for your ride and want to complete a route, you might come up short. As we continue to ride each route, we’ll be updating all of those “TBD”s in the tables in order to inform you of what you’re actually getting into when you click that big orange “Ride” button.

      +

      The World Hack

      It wasn’t that long ago that you only had one choice regarding which Zwift world you could ride on a given calendar day. Not too long ago, Zwift introduced the Guest World functionality, which keeps Watopia available every day, or allows you to choose one of the other worlds, which are available on a calendar-based rotation. Even more recently, the Guest World functionality has been further improved to allow you to choose from Watopia or two of the other worlds on any given day. But, even before the guest world functionality came into being, some clever folks figured out a hack that allows you to ride any world you like on any day. So, if your quest is to ride every route in every world, but don’t want to wait a few weeks for the opportunity to ride a route, you can make a simple configuration change that will allow you to ride wherever you want, whenever you want. If you are a PC user and comfortable with a bit of file editing, here’s how you do that:
      • Open Windows Explorer (press the Windows Key + E).
      • Navigate to Documents > Zwift.
      • Look for the prefs.xml file and open it with a text editor, such as Notepad.
      • After the first line, which reads <ZWIFT>, you’ll want to insert one line below it, as shown highlighted in the image below.
      • The number between the <WORLD> and </WORLD> tags (7 in the image below) can be changed to represent the world you want to ride in. The values you can use are as follows:
        • Watopia = 1
        • Richmond = 2
        • London = 3
        • New York = 4
        • Innsbruck = 5
        • Yorkshire = 7
      • Save the prefs.xml file.
      • Start Zwift.
      Once you reach the screen where you can choose your world/route, you’ll see that the world you selected is now the home world, and the regularly scheduled guest worlds are still offered on the second and third tabs.

      +

      Make Sure You Look Good Doing It, too!

      You may notice in some of our screenshots that we have our avatars dressed up in our gorgeous Slowtwitch cycling kit (the blue and white was the first generation, and the more colorful version, shown below, is the current generation). How do you get it? If you're on a PC or Mac, you can type "P" to enter a promo code. Enter SLOWTWITCH and you'll have the kit the next time you go into your garage - simple as that! For those on iOS devices, Apple TV, or Android devices, unfortunately, there isn't a way to enter a promo code.

      +

      Now Get Out and Explore!

      When you look at how much Zwift has grown over its 5 years of existence, it’s pretty amazing. We have a lot of options available to us now to help keep your indoor training experience fresh and fun. Keep an eye on this space and the map galleries linked in the table above for updates regarding the lead-ins and other information regarding the existing routes, as well as new routes as they become available. We’ll be continuing to explore the Zwift worlds and hope you’ll join us along the way! Ride on!

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Zwift_Routes_The_Runners_Only_Club_7660.html b/site/slowtwitch.com/www/Indoor_Training/Zwift_Routes_The_Runners_Only_Club_7660.html new file mode 100644 index 0000000..50cbd96 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Zwift_Routes_The_Runners_Only_Club_7660.html @@ -0,0 +1,693 @@ + + + + +Zwift Routes: The Runners Only Club - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Zwift Routes: The Runners Only Club

      + +
      +
      +
      +

      A few months ago, we took an inventory of all of the official cycling routes in Zwift. As a runner, you can lace up your shoes and run any of those routes, but Zwift offers a nice selection of run-only routes in addition to the routes you can share with our two-wheeled friends. If you’re new to running in Zwift, check out our primer on how to get started, as well as our review of the Runn Smart Treadmill Sensor. Lace up your shoes, connect your sensors, and get ready to explore the virtual worlds of Zwift on foot!

      Currently, there are 11 run-only routes in Zwift: 6 in Watopia and 5 in New York. From the 400m track at May Field in Watopia to the 9.2 mile Flat Irons course in New York's Central Park, the Zwift run-only routes offer a nice variety of sights and sounds for your entertrainment pleasure. The following table and gallery provide more details on each route.

      Running Routes



      World


      Route

      Length
      (miles / km)
      Elevation
      Gain
      (feet / meters)

      XP
      Earned
      New YorkCouch to Sky K4.2 / 6.8219 / 67260
      New YorkFlat Irons9.2 / 14.9479 / 146590
      New YorkHudson Roll5.6 / 9.0262 / 80360
      New YorkPark to Peak2.8 / 4.6410 / 125180
      New YorkShuman Trail Loop1.6 / 2.529 / 9TBD
      Watopia11.1 Ocean Blvd6.5 / 10.5154 / 47440
      Watopia5K Loop3.1 / 5.098 / 30200
      WatopiaChili Pepper4.9 / 8.0154 / 47320
      WatopiaJon’s Route7.8 / 12.5190 / 58500
      WatopiaMay Field.3 / .40 / 0TBD
      WatopiaThat’s Amore4.0 / 6.4177 / 54225


      Just like the cycling routes, you can earn Achievement Badges and bonus Experience Points (XP) for completing each of these routes. Your run profile is separate from your cycling profile, so you’ll earn XP at a different rate than you do as a cyclist, which helps you level up as a runner and earn new virtual run gear. Here’s a little tip for you, too: If you want to maximize your XP earnings, do running workouts - you’ll receive XP for covering the distance at the same rate per mile/km as you would if you weren’t doing a workout, plus you’ll earn XP for completing the segments of your workout. Zwift offers close to 50 pre-built run workouts, a run workout design tool so you can make your own, and 6 different run training plans. No matter what level of fitness you are at, you can find ways to enjoy Zwift on foot (you can walk, if you like, too!).

      Of course, Zwift provides plenty of virtual social opportunities for runners, too. You’ll find that there are several running events and challenges available any day, including run festivals, races, group runs, and monthly challenges. Running in Zwift is fun, so if you’re looking for something to spice things up on the ‘ol “dreadmill”, do yourself a favor and check out what Zwift has to offer!

      +

      + +

      + +

      Couch to Sky K

      World: New York
      Length: 4.2 miles / 6.8 km
      Elevation Gain: 219 feet / 67 m
      Bonus XP (Experience Points): 260

      Couch to Sky K includes a lap around the high roads of Central Park before you come back down to park level for a lap around the reservoir.

      +

      + +

      +

      Flat Irons

      World: New York
      Length: 9.2 miles / 14.9 km
      Elevation Gain: 479 feet / 146 m
      Bonus XP (Experience Points): 590

      Currently the longest run-only route in Zwift, Flat Irons consists of a trip around the Shuman Trail loop, a lap around Central Park, and one more trip around Shuman Trail.

      +

      + +

      +

      Hudson Roll

      World: New York
      Length: 5.6 miles / 9.0 km
      Elevation Gain: 262 feet / 80 m
      Bonus XP (Experience Points): 360

      This route includes a lap of the reservoir along with a trip around the Central Park inner loop.

      +

      + +

      +

      Park to Peak

      World: New York
      Length: 2.8 miles / 4.6 km
      Elevation Gain: 410 feet / 125 m
      Bonus XP (Experience Points): 180

      This relatively short route starts out in Central Park and takes you up the New York KOM. It is unique in that it is currently the only point-to-point run route in Zwift. Adjust your treadmill accordingly to maximize the effect!

      +

      + +

      +

      Shuman Trail Loop

      World: New York
      Length: 1.6 miles / 2.5 km
      Elevation Gain: 29 feet / 9 m
      Bonus XP (Experience Points): TBD

      A short, run-only loop around the reservoir in Central Park.

      +

      + +

      +

      11.1 Ocean Blvd

      World: Watopia
      Length: 6.5 miles / 10.5 km
      Elevation Gain: 154 feet / 47 m
      Bonus XP (Experience Points): 440

      As the name implies, this is 11.1 km that covers most of the run-only features of Watopia, including Run Valley, the beach path, and the ocean skyway. The advertised distance is 10.5 km when you fire up Zwift, but there is a bit of a lead-in before the start arch to get you the full 11.1 km.

      +

      + +

      +

      5K Loop

      World: Watopia
      Length: 3.1 miles / 5.0 km
      Elevation Gain: 98 feet / 30 m
      Bonus XP (Experience Points):

      This route gives you a full 5 km run-only path, starting in Run Valley, heading along the beach path, over the ocean skyway and through the hills before returning to the start.

      +

      + +

      +

      Chili Pepper

      World: Watopia
      Length: 4.9 miles / 8.0 km
      Elevation Gain: 154 feet / 47 m
      Bonus XP (Experience Points): 320

      To add just a little bit of heat to your running recipe, the Chili Pepper route starts out in Run Valley before merging onto the cycling road and taking you through the volcano.

      +

      + +

      +

      Jon's Route

      World: Watopia
      Length: 7.8 miles / 12.5 km
      Elevation Gain: 190 feet / 58 m
      Bonus XP (Experience Points): 500

      The longest route in Watopia, Jon's route covers all of the run-only features of Watopia, along with the original Flat Route. You'll get a nice tour of the beach, ocean tunnels, Italian Villas and more.

      +

      + +

      +

      May Field

      World: Watopia
      Length: .3 miles / .4 km
      Elevation Gain: 0 feet / 0 m
      Bonus XP (Experience Points): TBD

      Named in honor of the guy who started it all, May Field is a 400 m track.

      +

      + +

      +

      That's Amore

      World: Watopia
      Length: 4.0 miles / 6.4 km
      Elevation Gain: 177 feet / 54 m
      Bonus XP (Experience Points): 225

      This route starts out in Run Valley and takes you through the Italian Villas via the cycling roads.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Zwift_The_Deeper_Dive_6650.html b/site/slowtwitch.com/www/Indoor_Training/Zwift_The_Deeper_Dive_6650.html new file mode 100644 index 0000000..21e5b9f --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Zwift_The_Deeper_Dive_6650.html @@ -0,0 +1,654 @@ + + + + +Zwift: The Deeper Dive - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Zwift: The Deeper Dive

      + +
      +
      +
      +

      [Note: This article was originally published in Autumn, 2017, and has been updated to reflect changes over the past 12 months. This is the 2nd of what will be 8ish articles on how to Zwift, the whole series binge-published over the next 2 weeks.]

      Zwift is not hard to join; not hard to commence; but it’ll take you a good while to plumb its depths. Last week I wrote you a primer on the easiest, least expensive Zwift entry point: using Virtual Power to gauge your effort. That primer gave you just enough to get you set up with the hardware and software you need to simply ride.

      Now that you’re turning the pedals and exploring the virtual world, let’s take a look at how you can interact with that world and the other people who occupy it.

      +

      Whoa… Where am I?

      One of the fun aspects of Zwift is exploring the different courses and locales available in the game. Zwift currently offers several different routes in 5 different locales: Watopia (Zwift’s original virtual world), London, England, Richmond, Virginia, New York City and Innsbruck, Austria (the last 2 added since we first wrote this a year ago). Thus far, we’ve just picked the default route for our discussion so far, but what if you want to change it up?

      The available course is dictated by the date, so on a given calendar date, everybody is riding the same locale. The course schedule is displayed on the right side of the Start screen, so you can plan ahead a bit if you want to ride a certain course.

      +

      Within each course, you can choose a specific route from the list of available routes. Within each locale, all of the routes are interconnected. As you ride, if you don’t change direction at turns, you will stay on the route you selected at startup. Some routes have to be earned - for example, you must achieve level 10 to unlock the Jungle Circuit.

      +

      What is all this Stuff on the Screen? It’s the Zwift HUD (Head’s Up Display)

      Once you’ve chosen your route and started your ride, you may find the amount of information on screen overwhelming at first. The Zwift HUD (Head’s Up Display) provides a lot of information about your current ride. Let’s break the screen down:

      +

      Sensor Information (1)
      In the upper left corner, you’ll see your sensor information. This includes your wattage, and if you have other sensors paired, you can see your RPM and heart rate.

      Ride Statistics (2)
      In the top middle of the screen, you will see the values that are derived from your sensors in conjunction with the virtual world itself. There are quite a few bits of information here:

      Speed - Your speed is calculated based on your power, your weight, and the course. If you are putting out a hard effort to climb a hill with a 10% grade in Zwift, your bike computer may register 20 mph but Zwift may only read 5 mph. This is Zwift simulating the terrain and conditions. With zPower, you don’t feel the increased resistance going up a hill or decreased resistance going downhill - you just keep putting out the work and your progress will be calculated accordingly. If you want to get a more realistic feel, you’ll need a smart trainer.

      Mileage - The distance you’ve ridden in Zwift during this ride.

      Elevation - How many feet or meters of elevation you’ve gained during this ride.

      Elapsed Time - How long you’ve been riding.

      Progress Bar - The Orange and gray bar indicates how much progress you are making towards your next rider level. In the image above, the rider is at level 1 and is roughly half way to level 2. Your rider level is one of several motivational features in Zwift we’ll discuss in a future installment.

      Map (3)
      A map is displayed in the upper right corner. You can click/touch on the map to toggle 3 different map views. This is also where you will see the grade of the incline or decline you are currently riding.

      Riders Nearby (4)
      On the right side of the screen, you will see a list of riders that are near you. You can see how far ahead or behind the riders nearby are. If you click on a rider’s name, you will switch views so you can watch them. A “Back to Me” button will be displayed in the lower left corner of the screen so you can get back to your own ride. You may also see some other ways to interact with others here - you may see messages sent from other riders, and you may also have the opportunity to give somebody a thumb’s up (aka, a “ride on!”) when they reach an achievement.

      Power Ups (5)
      The white circle is where you can see your available Power ups. Power ups are another fun feature you earn randomly when you pass through different checkpoints. You can use the selected power up by hitting the spacebar on your keyboard, or touching the power up circle on an iPad.

      Messages (6)
      This is another motivational tool we’ll discuss shortly.

      In addition to the aforementioned display areas, There are a few other things that will pop up on screen at times. One of the more useful ones that you can interact with is the intersection indicators that will show up at the bottom of the screen:

      +

      These will be displayed as you are approaching an intersection. By default, one of the directions will be pre-selected (outlined in orange) based on the route you chose during the ride startup, and you don’t need to do anything - just keep riding and you’ll go that way. But if you want, you can change direction by clicking on the non-selected icon or using the Left/Right arrows on the keyboard, or touching the icon on your iPad.

      Another way to change direction is to do a U-Turn: You can do that with the Down arrow on the keyboard. Doing a U-turn at 30mph on gravel is pretty impressive!

      +

      OK, I’m riding and I like what I see, but how do I drive this thing?

      Now that you’re more familiar with what you’re seeing, let’s talk about how you take control. Zwift provides a lot of in-game options which are accessed via keyboard shortcuts on the PC or Mac, or via a touch menu on the iPad. Zwift provides different camera views, rider actions, workout controls, direction controls, messaging, and other ride options using keyboard shortcuts or a touch menu on iOS devices, as shown in the iPad Mini screenshot above.

      PC and Mac Keyboard Shortcuts

      Camera Angles
      1: Default view
      2: Third-person view
      3: Rider perspective
      4: Side view
      5: Rear view
      6: Head on view
      7: Spectator view
      8: Helicopter view
      9: Bird’s eye view
      0: Panorama version of current view

      Rider Actions
      F1: Stick out elbow
      F2: Wave hand
      F3: “Ride On!”
      F4: “Hammer Time!”
      F5: “Nice!”
      F6: “Bring It!”
      F7: “I’m toast”
      F8: Bike bell

      Direction Controls
      Down Arrow: U-turn
      Left Arrow: Left Turn
      Right Arrow: Right Turn

      Other Controls
      Spacebar: Use power up
      ESC: Display “End Ride” screen
      F10: Screenshot
      A: Device pairing screen
      G: Toggle Watt/HR graph
      M: Group message
      P: Enter a promo code
      T: User Customization screen
      Up Arrow: Show actions/options menu (use left/right arrows to select)

      Workout Controls
      E: Display workout selection screen
      Tab: Skip workout block
      Page Up: Decrease workout intensity
      Page Down: Increase workout intensity

      For your convenience, we’ve made a handy printable cheat sheet that you can download.

      +

      iOS Controls

      The iOS app is full-featured, and utilizes a touch screen interface. If you click on the screen while you ride, a control menu will be displayed (above).

      You can scroll left and right for more options. Touching the camera icon will toggle through the different views, and you can touch anywhere outside the on-screen menu to hide it. I found that I prefer the iOS version on the iPad mini because it’s easier to touch a screen instead of pressing a key when moving and sweating.

      Hey, I want a kit like that guy had!

      By now, you have probably noticed a wide variety of different jerseys, bikes, and rider appearances as you encounter other riders within Zwift. Let’s make sure we’re looking good while we’re at it! Zwift allows you to customize how your avatar looks - you can change kits, bikes, and other rider attributes. You can get to the user customization menu from two places:

      - Press T during your ride
      - Pause the ride, and select “Customize” on the right side of the Pause screen.

      Either of these methods will bring up the User Customization screen. On the left side you can select different aspects of your avatar that you want to customize - your kit, your bike, and the appearance of your avatar. In the beginning, you have a fairly limited selection of gear options, but as you continue to ride, you will earn new gear - kits, bikes, wheels, socks, and more.

      +

      If you really want to look good, you need the Slowtwitch kit. To get the kit, type P during your ride to enter a promo code. Enter SLOWTWITCH and press Enter. The Slowtwitch kit is now all yours - wear it with pride.

      +

      I just hit the “I’m Toast!” key. I wanna go home. How do I get out of here?

      Once you are ready to wrap up your ride, you can simply coast to a stop. Your avatar will put a foot down and stand still. The Menu button is then displayed in the lower left corner of the screen. Alternatively, you can hit the ESC key on your keyboard to end your ride.

      After your ride, you’ll see your statistics:

      +

      Hitting the End Ride button will allow you the opportunity to save the ride to your history and share it on Strava, MapMyRide, Garmin Connect and others.

      +

      It probably occurred to you that I wasn't entirely transparent. I called this a "deeper dive" which it was. But in reality we’ve just scratched the surface with Zwift. This app is cycling's Dungeons and Dragons, but executed at some percentage of your FTP with your feet turning circles. Our goal above was to add some fun to your first ride in Zwift. Very soon we’ll look at some more advanced functions within Zwift to enhance the ride and familiarize ourselves with the Zwift virtual world. We will look at the different ways Zwift provides motivation and allows us to interact with other people. We will take our Zwift experience beyond just a ride and learn how to perform workouts and share information with a coach or friends.

      Ride on!

      Here's the whole series:

      1. Welcome to Watopia: Install Zwift using virtual power, the most cost-effective option.
      2. Zwift, The Deeper Dive: One more layer below the surface.
      3. It's Game On With Zwift: Zwift is a game, but also a virtual community.
      4. Zwift: Let's Dance! The social aspect; Zwift is a Massive Multiplayer Game.
      5. Zwift: A Potent Tool for Training: Zwift’s workout mode.
      6. On Zwift Every Day is Race Day: Zwift's racing culture and community.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/Zwift_Welcome_to_Watopia_6642.html b/site/slowtwitch.com/www/Indoor_Training/Zwift_Welcome_to_Watopia_6642.html new file mode 100644 index 0000000..d3ea57f --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/Zwift_Welcome_to_Watopia_6642.html @@ -0,0 +1,658 @@ + + + + +Zwift: Welcome to Watopia - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Zwift: Welcome to Watopia

      + +
      +
      +
      +

      [Note: This article was originally published in Autumn, 2017, and has been updated to reflect changes over the past 12 months. This is the 1st of what will be 8ish articles on how to Zwift, the whole series binge-published over the next 2 weeks.]

      With Zwift, cycling is a game. But it’s still cycling! Which makes it a purposeful game, whether your interest is training or racing. Let’s talk about how you can get Zwift installed and configured easily and inexpensively so you can ride in the Zwift virtual world.

      We'll focus on the use of a Speed and Cadence Sensor, a $60 gadget you put on your bike that communicates with the "Game" (as Zwift calls its program), and is the most economical way to Zwift. We'll sift in descrptions of higher-tech options, like a power meter or a smart trainer.

      +

      Alright, I’m interested in this whole Zwifting thing. Where do I start?

      Let's get up and running with Zwift using the most minimal approach possible. We just want to ride. This is for the person who wants to ride and have fun without taking out a second mortgage on the house or selling organs on the black market (you’ll likely need your organs for riding with Zwift, anyway). Using Zwift doesn't require a power meter or smart trainer. You can do just fine with your old trainer thanks to a concept called "Virtual Power".

      +

      What is Virtual Power?

      Some really smart people have figured out that an estimated power number can be derived from the rider’s weight, speed, tire size, and the resistance curve of the rider’s specific trainer. Every trainer has its own personality that can be described as its resistance curve. Regardless of the resistance type - wind, magnetic, fluid, or electromagnetic - that curve can be measured and mapped. Virtual Power has been a revolutionary development in indoor training.

      How good is Virtual Power? It’s not perfect, but short of owning a power meter or a smart trainer, it’s going to do the job. There are a few ways to make sure Virtual Power is as reliable and as accurate as it can be, and we'll talk about that in a minute. Suffice it to say it does what we need it to do.

      +

      What Equipment Do I Need?

      In order to ride with Zwift, you need the following hardware:

      - A bike
      - An ANT+ or Bluetooth speed sensor
      - A stationary trainer or rollers
      - A computer
      - An ANT+ receiver (aka, “dongle”) if you're communicating using ANT+

      Beyond these, you will probably want a few accessories to make your pain cave less painful - such as a wheel riser to bring the front wheel up to level (that old college textbook you never read will work), a trainer mat to protect your flooring, a stand for your computer or tablet, so everything is visible and within reach, a fan, and a towel. If you or your coach want more numbers you may also want a cadence sensor and heartrate monitor, and you may find convenient a USB extension cord to place your ANT+ dongle. But these are electives, and not mandatory.

      Stationary Trainer: For our initial Zwift experience, we’re using virtual power, so you can use basically any non-”smart” trainer or rollers. The level of support and accuracy varies, so it is best to refer to Zwift’s knowledge base of trainers.

      Zwift has two levels of Virtual Power: Classic power and zPower. zPower is the more precise option, where the power curve has been calibrated by the Zwift team and is limited to a few select models now. Classic power is less precise, but increases the chance that your trainer will be supported.

      Speed Sensor: For speed, you need an ANT+ or Bluetooth speed sensor mounted at the rear wheel. A couple of popular options are available from Garmin or Wahoo. The Garmin speed sensor is part of a speed/cadence sensor bundle, while the Wahoo RPM speed sensor is available by itself. These newer sensors allow for an easy install on the rear hub, which is nice if you want to move them from bike to bike. There are other compatible sensors that mount to the chainstay, like the older Garmins. There are also sensors that are proprietary and will not work. As long as your sensor transmits via ANT+ or Bluetooth (which should be indicated on the sensor itself), you’re good to go.

      +

      Receiver/Dongle: Your speed sensor now needs to have a way to communicate with your computer. How you make this connection depends on if you are using ANT+ or Bluetooth.

      If you have any ANT+ devices, you will need an ANT+ dongle. If you have other fitness equipment, you might already have one. There's a bunch of them on the market, and you can get them for about $20 from a supplier such as Digikey. If you have any connectivity issues, you can improve your connection by having a fan to circulate air, using a USB extension cable to place the dongle closer to the speed sensor, or removing other electronic devices from the area.

      +

      For the Bluetooth user, you have a bit different way of connecting and that depends upon what kind of device you will run Zwift, so let’s jump into that discussion.

      A Computer, Tablet, or Smartphone: With all the hardware in place, you now need a home for the software. Zwift can be installed on a PC or Mac meeting the minimum hardware requirements, or a compatible Apple iOS device. If you have a Bluetooth speed sensor, you can connect directly to your iOS device without any kind of extra dongle. If you have a Bluetooth speed sensor, you can also use a Bluetooth-capable Android or iOS smartphone to bridge the gap using the Zwift Mobile Link iPhone/Android app. So, there are a few ways to connect:

      1. ANT+ speed sensor → ANT+ USB dongle → PC or Mac
      2. Bluetooth speed sensor → Smartphone with Zwift Companion App → PC or Mac
      3. Bluetooth speed sensor → iOS device (iPad)

      Finally, you will need to maintain an Internet connection while riding Zwift.

      Here is what I used for the purposes of this article, and I installed Zwift on two different computers or devices:
      - Kurt Kinetic Road Machine trainer
      - Wahoo RPM Speed Sensor (transmits both Bluetooth and ANT+)
      - DigiKey ANT+ Dongle

      Windows computer:
      - Zwift installed on HP Envy Laptop
      - Intel Core i5-3317U CPU at 1.70 GHz with 2 cores
      - Windows 10
      - 16 GB of memory
      - Intel HD Graphics 4000 GPU

      iOS device:
      - Also installed on iPad Mini 4
      - iOS version 10.3.3 (we're now up to 12.0.1. as of this writing and everything is still hunky dory).

      With your trainer, sensors, dongles, and computer in place, you now have the foundation for virtual training. If you shop around, you can get everything here minus the computer for under $300. Note that this hardware setup is not limited to use with Zwift - you can use a variety of applications with this virtual power setup. Look for a few more articles covering other options soon.

      Alright, I got the hardware. Gimme Zwift!

      Now that you have the hardware, you are ready to obtain the software. Zwift offers a free 7-day trial and you just download the version you need from their home page. The download is fairly quick and small, but note that the actual installation is going to take a while. The initial installation is your fairly typical Windows installation - agree to the user terms and conditions, choose where you want Zwift installed, and click finish. This doesn’t take long at all. The first time you go to run Zwift, the startup process will go out and retrieve additional components over the Internet. This takes a little bit longer. Using a Windows laptop over a good WiFi network with a high-speed Internet connection took about 30 minutes from starting the download to being able to get ready to ride. Installing the iOS app on an iPad using the same user profile didn’t take long at all. So, keep that in mind if you’re excited to get going and ride the first time.

      During the startup process, you have the opportunity to log in:

      +

      In our case, we’re a new user, so we’ll click the Create New Account link and get ourselves set up as a Zwift user. First, we’ll enter some basic profile info.

      +

      And then we’ll add a little bit more information about ourselves:

      +

      When setting up your profile, you will want to be honest about yourself. Zwift is interactive and offers ways to earn points and other rewards as you ride, so you are competing with others for KOMs, etc. Your weight is important for not only the virtual power calculation, but the integrity of your results. Don’t be a zDoper.

      Once your account is set up, you can make changes in the application or on the Zwift website.


      It’s Time To Ride! Almost…

      Before you can ride, you need to get a couple of things ready. First, get your bike installed on the trainer. With Virtual Power like zPower, it is important to make sure you do the following:

      - Always inflate your tire to the same pressure.
      - When tightening down your trainer on your tire, always tighten it at the same place on the tire. The stem makes for an easy reference point.
      - When tightening down your trainer on the tire, bring the tension unit up to the tire, and then crank it down the same number of turns every time. You want to make sure you have tightened it down enough that the tire does not slip.

      Following these few tips will help to ensure that virtual power is as consistent as possible.

      Assuming everything is plugged in and operational, hop on the bike, start to spin, and fire up Zwift. The first screen you encounter in Zwift is the Device Pairing screen:

      +

      Since this is our first ride, your screen will look like the one above. If you hesitate, things start to get a little weird - a squirrel starts to talk to you. In case you’re wondering, the squirrel’s name is Scotty. At this point, we can click the “Just Watch” button to watch other people having fun and getting in a good workout:

      +

      But, we’re here to ride! Click on the appropriate buttons to pair any devices you have. For our Virtual Power setup purposes, we will select the Search button under the “Speed Sensor + Classic Trainer” icon. Zwift will now identify any devices it finds via ANT+ and Bluetooth, and you will see the following screen:

      +

      Once your speed sensor is paired, select the wheel/tire size closest to yours and click OK. You will now be prompted to select your trainer:

      +

      You will see that there are quite a few trainers specified. Since we are using zPower, you won’t see any smart trainers here. Those are paired under a different option on the Device Pairing screen. You will see a really good selection of trainers from the last few years, but if you don’t find your trainer or rollers, there is an option for “other” at the bottom. This will likely be less accurate than having a specific supported trainer, but it will work. In this case, we have selected a Kurt Kinetic Road Machine. Select your trainer and click OK to return to the Device Pairing screen. The Device Pairing screen will now indicate how you are connected:

      +

      In this case, we are also paired up with a heart rate monitor and a cadence sensor, which are optional for riding purposes. All of the devices are connected via ANT+ as shown in the lower right corner of each section. We can see that they are working, as the measured values are being shown. Let’s click OK to hit the last screen before we ride:

      +

      There is quite a bit going on here. For our purposes, we are just going to take the defaults. We will cover all of this in more detail in a future article. Click the Ride button.

      I’m Zwifting! I’m Zwifting! I’m a Zwifter! I Zwift!

      Congratulations! You are now riding Zwift using Virtual Power in Free Ride mode. You may already be pedaling, so your character (your “avatar”) will pedal accordingly. If you’ve got the sound turned up, you can even hear them/you clip in.

      +

      Once you overcome the initial rush of seeing your virtual you pedaling along down the road, you’ll notice there is a lot of other information on the screen - also known as the Zwift HUD (Heads Up Display).

      But that's enough for now! We'll cover all this in a second installment that will follow close on the heels of this one. We'll decipher the Zwift HUD; explain what you do when you come to intersections; we'll discuss the ride statistics after you've finished; and then we'll talk about how to share your ride on MapMyRide, Strava, GarminConnect and others.

      If you want to take a next step, here's a deeper dive into Zwift.

      Here's the whole series:

      1. Welcome to Watopia: Install Zwift using virtual power, the most cost-effective option.
      2. Zwift, The Deeper Dive: One more layer below the surface.
      3. It's Game On With Zwift: Zwift is a game, but also a virtual community.
      4. Zwift: Let's Dance! The social aspect; Zwift is a Massive Multiplayer Game.
      5. Zwift: A Potent Tool for Training: Zwift’s workout mode.
      6. On Zwift Every Day is Race Day: Zwift's racing culture and community.

      And, for extra credit...

      7. ERG Mode in Zwift: Handy for ride leaders, and/or when your legs aren't quite up to it.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/index.html b/site/slowtwitch.com/www/Indoor_Training/index.html new file mode 100644 index 0000000..25833de --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/index.html @@ -0,0 +1,915 @@ + + + +<:: Welcome to Slowtwitch.com ::> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + + +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +

      Watts Up in Watopia: Mountains, Beta Races, and More

      +
      +: + Zwift's got something for everybody this week. +
      +
      +
      +
      +
      + +
      +
      +

      We Noticed: Kanberra Sport

      +
      +: + The Team Suunto partner is a relative unknown in the endurance sport world. Start paying attention. +
      +
      +
      +
      +
      + +
      +
      +

      Watts Up in Watopia: Ride with Jeremiah Bishop and New Roads

      +
      +: + The virtual Tour de France results in lots of fresh tarmac in Zwift. +
      +
      +
      +
      +
      + +
      +
      +

      Attacking Alpe du Zwift

      +
      +: + Two cyclists go head to head on the slopes of Alpe du Zwift. +
      +
      +
      +
      +
      + +
      +
      +

      Tips and Tricks for IRONMAN's VR Championship Series

      +
      +: + Just like an in-person race, you need to sweat the details if you're competing for 70.3 World slots. +
      +
      +
      +
      +
      + +
      +
      +

      The Smart Bike as a Home Fit Tool

      +
      +: + This is the result of my experiment, using smart bikes to inform the position on my outdoor bikes. +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +

      Watts Up in Watopia This Week? Our New Zwift Preview Article

      +
      +: + We're not just previewing our own events: find the exact group ride, race, or run you're looking for. +
      +
      +
      +
      +
      + +
      +
      +

      Zwift Route Maps: Innsbruck

      +
      +: + (Updated 5/10/2020) This gallery includes a map of each of the routes in the Innsbruck world in Zwift. +
      +
      +
      +
      +
      + +
      +
      +

      Zwift Route Maps: Watopia (Part 1)

      +
      +: + (Updated 5/9/2020) If you like to explore, we’ve got just the thing for you: This gallery includes a map of each of the routes in the Watopia world in Zwift. +
      +
      +
      +
      +
      + +
      +
      +

      Zwift Route Maps: Watopia (Part 2)

      +
      +: + (Updated 5/9/2020) If you like to explore, we’ve got just the thing for you: This gallery includes a map of each of the routes in the Watopia world in Zwift. +
      +
      +
      +
      +
      + +
      +
      +

      Zwift Route Maps: New York

      +
      +: + (Updated 5/8/2020) If you like to explore, we’ve got just the thing for you: This gallery includes a map of each of the routes in the New York world in Zwift. +
      +
      +
      +
      +
      + +
      +
      +

      SMART Indoor Training: You Are Here

      +
      +: + In our first two installments of the SMART Indoor Training series, we discussed how we use measurement as a key element of defining our goals. SMART is an acronym for “Specific”, “Measureable ... +
      +
      +
      +
      +
      + +
      +
      +

      Zwift Route Maps: Richmond

      +
      +: + (Updated 5/7/2020) If you like to explore, we’ve got just the thing for you: This gallery includes a map of each of the routes in the Richmond world in Zwift. +
      +
      +
      +
      +
      + +
      +
      +

      Faster With Friends: TrainerRoad Group Workouts

      +
      +: + I had the pleasure of doing my first TrainerRoad group workout with TrainerRoad’s very own Communications Director, Jonathan Lee. With Jonathan in his garage in Reno, Nevada, and me in my fit ... +
      +
      +
      +
      +
      + +
      +
      +

      Zwift Route Maps: Yorkshire

      +
      +: + (Updated 5/6/2020) If you like to explore, we’ve got just the thing for you: This gallery includes a map of each of the routes in the Yorkshire world in Zwift. +
      +
      +
      +
      +
      + +
      +
      +

      Zwift Route Maps: London

      +
      +: + (Updated 5/5/2020) If you like to explore, we’ve got just the thing for you: This gallery includes a map of each of the routes in the London world in Zwift. +
      +
      +
      +
      +
      + +
      +
      +

      Zwift Routes: Oh, the Places you'll Go!

      +
      +: + (Updated 5/5/2020) One of the fun aspects of a game-like application like Zwift is that you are not bound to the constraints of the real world. In their efforts to enhance our indoor training ... +
      +
      +
      +
      +
      + +
      +
      +

      The Birth of Zwift

      +
      +: + "I should probably turn this thing into a product, but for now, it's just used by me." +
      +
      +
      +
      +
      + +
      +
      +

      This Week on Slowtwitch Indoors: Slowtwitch Staff Thursday

      +
      +: + Join our team of editors, writers, and contributors across our Thursday events this week. +
      +
      +
      +
      +
      + +
      +
      +

      Zwift Routes: The Runners Only Club

      +
      +: + If you want to add some fun to your indoor running, Zwift offers a nice selection of routes just for you! Let’s take a look at the run-only routes currently available in Zwift. +
      +
      +
      +
      +
      + +
      +
      +

      This Week on Slowtwitch Indoors: Flat and Fast

      +
      +: + This week's rides and runs feature Fuego Flats. +
      +
      +
      +
      +
      + +
      +
      +

      Update on the RUNN Treadmill Sensor

      +
      +: + I tested two things: does it work on curved treadmills? And just how accurate is this device? +
      +
      +
      +
      +
      +
      +
      Prev Next
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/more2.html b/site/slowtwitch.com/www/Indoor_Training/more2.html new file mode 100644 index 0000000..d2a5e77 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/more2.html @@ -0,0 +1,898 @@ + + + +<:: Welcome to Slowtwitch.com ::> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +
      +

      Indoor Training

      +
      +
      Prev Next
      +
      +
      +
      +
      + +
      +
      +

      Getting Started with Ironman VR

      +
      +: + IRONMAN has stepped into the virtual racing arena with their new IRONMAN VR race series. What do you need to participate? Let's find out! +
      +
      +
      +
      +
      + +
      +
      +

      Tacx NEO Bike Long Term Review

      +
      +: + Here is everything good, and everything less good, about one of the industry leading products of 2020 +
      +
      +
      +
      +
      + +
      +
      +

      Every Thursday, it's Jimmie's Day

      +
      +: + Jimmie Will Richey has developed a cult Zwift following while leading Thursday's Hilly Vanilli D Group. +
      +
      +
      +
      +
      + +
      +
      +

      Saris MP1 Platform

      +
      +: + There is one product for stationary that is different than any other. This platform makes stationary riding feel more like outdoor riding. +
      +
      +
      +
      +
      + +
      +
      +

      This Week on Slowtwitch Indoors: Exploring Watopia

      +
      +: + All but one event this week takes place in Zwift's original virtual world. +
      +
      +
      +
      +
      + +
      +
      +

      To Erg Or Not To Erg

      +
      +: + I wrote this about the same time that a thread - "Why you need to quit ERG mode..." - appeared on our forum. I didn't write it in response that thread; it was just a happy coincidence. This is the ... +
      +
      +
      +
      +
      + +
      +
      +

      Yes, You Can Change Your Smart Trainer to 12-speed

      +
      +: + Here's the cost and the process to migrate almost any smart trainer from 11sp to 12sp. +
      +
      +
      +
      +
      + +
      +
      +

      This Week on Slowtwitch Indoors: Introducing Casual Fridays

      +
      +: + It's not about the dress code. It's the newest addition to our Zwift ride calendar. +
      +
      +
      +
      +
      + +
      +
      +

      Virtual and Actual Bike Choices for Stationary Racing

      +
      +: + Whether Zwift or IRONMAN VR, your actual stationary bike choice, your virtual bike choice, and your rider position will make a difference. +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +

      Do the NEO Bike and the Garmin Vector 3 Power Numbers Match?

      +
      +: + Historically NEOs and Vectors did not have a great reputation for generating congruent power numbers. What about now? +
      +
      +
      +
      +
      + +
      +
      +

      This Week on Slowtwitch Indoors: Let's Go Racing!

      +
      +: + We take on the crown jewel of Zwift climbing for our first-ever racing event this Thursday. +
      +
      +
      +
      +
      + +
      +
      +

      Smart Bike Shootout

      +
      +: + For me, the KICKR Bike is the product of the year. Or it's the NEO Bike. Each is way ahead of whatever is in third place. +
      +
      +
      +
      +
      + +
      +
      +

      This Week on Slowtwitch Indoors: To the Top!

      +
      +: + Bring your climbing legs. We're attacking some of Zwift's biggest climbs. +
      +
      +
      +
      +
      + +
      +
      +

      The RUNN Wireless Treadmill-to-Zwift Sensor

      +
      +: + This $99 unit has advantages over a footpod, if you're a Zwift runner. +
      +
      +
      +
      +
      + +
      +
      +

      Virtual Racing Today: Ironman and Others

      +
      +: + The Western World is one week into social isolation; Ironman and others rush to fill the void in virtual communities and platforms. +
      +
      +
      +
      +
      + +
      +
      +

      Getting to Know Slowtwitch Indoors

      +
      +: + Joining Zwift for the first time? Looking for new rides to fill your schedule? Meet our slate of events. +
      +
      +
      +
      +
      + +
      +
      +

      The Kickr Bike: Long Term Review

      +
      +: + Since I’m writing about the Kickr Bike today my time to give it back is approaching, and this won’t be easy. +
      +
      +
      +
      +
      + +
      +
      +

      Pain Cave Displays

      +
      +: + I’m beyond certain there are better, cleaner, sexier Pain Caves out there. I’m not the Cave expert. Maybe a Pain expert. +
      +
      +
      +
      +
      + +
      +
      +

      This Week on Slowtwitch Indoors: Return to Watopia

      +
      +: + All of our rides and runs take place in Zwift's original virtual world. +
      +
      +
      +
      +
      + +
      +
      +

      This Week on Slowtwitch Indoors: May (Field) Day!

      +
      +: + Our runs move to Watopia's track for the foreseeable future. +
      +
      +
      +
      +
      + +
      +
      +

      This Week on Slowtwitch Indoors: A New York State of Mind

      +
      +: + Our Zwift rides and runs head to the Big Apple. +
      +
      +
      +
      +
      + +
      +
      +

      SMART Indoor Training: Setting Goals (Your "A" Race)

      +
      +: + We have a new holiday: Quitter’s Day. According to data analysis performed by the team at Strava, Quitter’s Day is the day that most New Year’s fitness resolutions fail. This year, we ... +
      +
      +
      +
      +
      + +
      +
      +

      This Week on Slowtwitch Indoors: Go Fast, Take Chances

      +
      +: + Inspired by Anthony Famiglietti's 2-mile run at Endurance Exchange, we're ready to bring the pace this week on Zwift. +
      +
      +
      +
      +
      + +
      +
      +

      SMART Indoor Training: How Do You Measure Up?

      +
      +: + You may have heard of SMART goal setting. SMART is an acronym for Specific, Measurable, Attainable, Realistic, and Timely - attributes that all goals need to have. This is the first of a handful of ... +
      +
      +
      +
      +
      + +
      +
      +

      Spend Time With the Fam!

      +
      +: + We have a special Slowtwitch Zwift event this Friday. It's a run with 2X Olympian Anthony Famiglietti. But it's a lot more than that. +
      +
      +
      +
      +
      +
      +
      Prev Next
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/more3.html b/site/slowtwitch.com/www/Indoor_Training/more3.html new file mode 100644 index 0000000..bf3dd0a --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/more3.html @@ -0,0 +1,898 @@ + + + +<:: Welcome to Slowtwitch.com ::> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +
      +

      Indoor Training

      +
      +
      Prev Next
      +
      +
      +
      +
      + +
      +
      +

      This Week on Slowtwitch Indoors: Explore the World

      +
      +: + We'll be visiting Watopia, Innsbruck, New York, and Bologna this week. +
      +
      +
      +
      +
      + +
      +
      +

      This Week on Slowtwitch Indoors: Second Verse, Same as the First

      +
      +: + The 100/100 Challenge marches on, and our Tuesday rides swap their workouts. +
      +
      +
      +
      +
      + +
      +
      +

      This Week on Slowtwitch Indoors: More Miles, More Smiles

      +
      +: + We welcome in 2020 by spending plenty of time in Watopia. +
      +
      +
      +
      +
      + +
      +
      +

      Welcome to Sufferlandria

      +
      +: + The Sufferfest started out with a series of videos that delved deep into the darker humor of the “pain cave.” When you pressed Play on one of their videos - with titles such as “A Very Dark ... +
      +
      +
      +
      +
      + +
      +
      +

      When You Weren't Looking: Rouvy Revisited

      +
      +: + It's been about a year since we spent some time with Rouvy, so let's take a look at what has changed in 2019. Is Rouvy the right training tool for you for 2020? +
      +
      +
      +
      +
      + +
      +
      +

      Where Are the Runners on Zwift? (I’ll Show You.)

      +
      +: + In a couple of clicks you can migrate to where the Zwift runners are. +
      +
      +
      +
      +
      + +
      +
      +

      This Week on Slowtwitch Indoors: Feeling the Burn

      +
      +: + This week's Zwift sessions will give you your money's worth. +
      +
      +
      +
      +
      + +
      +
      +

      Slowtwitch 12 Days of Xmas, Tacx NEO 2T

      +
      +: + The very quiet smart trainer that provides real world feel. +
      +
      +
      +
      +
      + +
      +
      +

      This Week on Slowtwitch Indoors: The Need for Speed

      +
      +: + Our Zwift events have been feeling frisky with plenty of pace. This week is no exception. +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +

      Press Release: Zwift Takes Things Off Road

      +
      +: + Zwift is now offering new dedicated MTB and gravel training plans as well as in-game equipment to better cater to off-road cyclists. +
      +
      +
      +
      +
      + +
      +
      +

      Zwift Meetups: So Happy Together!

      +
      +: + We touched briefly on Meetups in our introduction to the Zwift Companion app a couple of weeks ago. We promised we would talk about this feature a bit more in the near future, so let’s dive in! +
      +
      +
      +
      +
      + +
      +
      +

      This Week on Slowtwitch Indoors: And Now We Run

      +
      +: + Weekly Zwift runs join the schedule, doubling our weekly event calendar. +
      +
      +
      +
      +
      + +
      +
      +

      Cranking Up the Eye Candy: The Pain Cave in Ultra 4K

      +
      +: + You've put together the ultimate pain cave, but are you getting the most out of your indoor training application? We're going to look at what it takes to maximize the visual experience of indoor ... +
      +
      +
      +
      +
      + +
      +
      +

      Slowtwitch 12 Days of Xmas, Feedback Sports Omnium

      +
      +: + This tiny trainer throws punches above it's weight. +
      +
      +
      +
      +
      + +
      +
      +

      Hand Me the Remote, please: An Introduction to the Zwift Companion App

      +
      +: + Zwift offers a neat Companion application that acts as your remote control for all things Watopian. Let's have a look at the functionality it provides! +
      +
      +
      +
      +
      + +
      +
      +

      This Week on Slowtwitch Indoors: All About the Hills

      +
      +: + Earn (or burn off) the bird with some quality climbing efforts on Zwift. +
      +
      +
      +
      +
      + +
      +
      +

      This Week on Slowtwitch Indoors: Race Simulations

      +
      +: + Whether you're in the midst of cyclocross season or trying out Zwift racing, our Zwift rides this week will help get you ready. +
      +
      +
      +
      +
      + +
      +
      +

      When You Weren't Looking: What's New in Zwift

      +
      +: + It's that time of year where you might be retreating back to the pain cave and getting back into Zwifting. Let's have a look at what has changed while you were away. +
      +
      +
      +
      +
      + +
      +
      +

      Zwift: It Do Run Run Run!

      +
      +: + With the popular 100/100 Run Challenge starting in a couple of weeks, some of the members of the Slowtwitch community will be hitting the good ol’ “dreadmill.” Why not make it a bit more fun? ... +
      +
      +
      +
      +
      + +
      +
      +

      Upscaling Your Pain Cave with the Saris MP1 Nfinity

      +
      +: + It's an unprepossessing look, just sitting there. But it comes alive when you're aboard it. +
      +
      +
      +
      +
      + +
      +
      +

      Smart Trainers 2020: Direct-Drive Trainers

      +
      +: + We continue our inventory of the current smart trainer market by looking at the direct-drive options available as we head into 2020. Let's have a look at the expanding selection of products made to ... +
      +
      +
      +
      +
      + +
      +
      +

      Smart Trainers 2020: Rear-Wheel Trainers

      +
      +: + With indoor season just around the corner (at least in the Northern Hemisphere), we wanted to put together an inventory of all of the smart trainer options on the market. In this installment, we ... +
      +
      +
      +
      +
      + +
      +
      +

      Wahoo Kickr Bike: On the Leading Edge of a Hot New Category

      +
      +: + If Peloton's bike had never existed, the new adjustable smart bikes by Tacx, Stages, Wattbike, Wahoo would still have been made. +
      +
      +
      +
      +
      + +
      +
      +

      It’s a VirtuGo-a-Go-Go!

      +
      +: + In today’s indoor training landscape, we have a lot of good options to entertain and train - or as I like to describe it, “entertrain”. In the last few years, we have witnessed the rise of ... +
      +
      +
      +
      +
      + +
      +
      +

      It's Hot, Sunny, Great Riding Outdoors. Translation: How Do I Zwift?

      +
      +: + Late to the Zwift Party? No matter. Let’s get you up to speed right now. +
      +
      +
      +
      +
      +
      +
      Prev Next
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/more4.html b/site/slowtwitch.com/www/Indoor_Training/more4.html new file mode 100644 index 0000000..6b6de9e --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/more4.html @@ -0,0 +1,898 @@ + + + +<:: Welcome to Slowtwitch.com ::> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +
      +

      Indoor Training

      +
      +
      Prev Next
      +
      +
      +
      +
      + +
      +
      +

      Stationary Cycling Isn’t Seasonal

      +
      +: + Honestly, 25 years ago, we rode our Computrainers more in the spring/summer than we did in the winter. +
      +
      +
      +
      +
      + +
      +
      +

      Wahoo Acquires The Sufferfest

      +
      +: + Stationary cult favorite The Sufferfest will become part of Wahoo Fitness's suite of indoor cycling products. +
      +
      +
      +
      +
      + +
      +
      +

      This Virtual Week (May 20-27, 2019)

      +
      +: + "Factory Work" and "Road Trip" are two new workouts debuting this week. Is this your week you take the Zwift plunge? +
      +
      +
      +
      +
      + +
      +
      +

      This Virtual Week on Slowtwitch

      +
      +: + Did you notice Zwift on WAPO yesterday? That's almost as big as Zwift on Slowtwitch tomorrow! +
      +
      +
      +
      +
      + +
      +
      +

      This Week's SlowZwift Events

      +
      +: + Writing Zwift workouts takes a little panache I think. If you take pride in your work. +
      +
      +
      +
      +
      + +
      +
      +

      Delightful Masochism Playing on a Screen Near You

      +
      +: + It's my latest workout. See it (and ride it) on Tuesday. +
      +
      +
      +
      +
      + +
      +
      +

      This Virtual Week: Slowman Writes His First Zwift Workout

      +
      +: + This Virtual Week are weekly prompts on the virtual community events Slowtwitchers engage in over that week. +
      +
      +
      +
      +
      + +
      +
      +

      A Quick Peek at Road Grand Tours

      +
      +: + Multiplayer platforms are the hot ticket in stationary cycling. Here's one you'll want to know about. +
      +
      +
      +
      +
      + +
      +
      +

      Garmin Buys Tacx

      +
      +: + Both companies had a need. But both companies had a similar problem. Wahoo is the presence in the background of this acquisition. +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +

      On Zwift Every Day is Race Day

      +
      +: + Did you know there's ZADA? Like WADA? And actual prizes for Zwift races? Results and rankings? Categories? +
      +
      +
      +
      +
      + +
      +
      +

      Rouvy! (You'll Like the Movie)

      +
      +: + We're running out rhymes for Rouvy. (Anybody wanna peanut?) But this 3rd installment tells you more about a hot new stationary platform. +
      +
      +
      +
      +
      + +
      +
      +

      Chuck Wurster Changed the World for You and Me

      +
      +: + Almost certainly the way you train on a stationary bike was heavily influenced by this man. +
      +
      +
      +
      +
      + +
      +
      +

      Trainerroad: The Birth of Structured Training

      +
      +: + Zwift is the apex of stationary entertainment. But structured training is still the need, and TR brought it to the masses. +
      +
      +
      +
      +
      + +
      +
      +

      Slowtwitchers Prefer Direct Drive Trainers

      +
      +: + They're moving in large numbers from one trainer style to another, though few state they're in the trainer market. +
      +
      +
      +
      +
      + +
      +
      +

      Goodbye Rouvy Tuesday

      +
      +: + In Part-2 of our series on Rouvy, we explore virtual rides, a prime feature of this stationary platform. +
      +
      +
      +
      +
      + +
      +
      +

      ERG Mode in Zwift

      +
      +: + Do you find it hard to hang in Slowzwift Structured Training Events? Here's a secret. +
      +
      +
      +
      +
      + +
      +
      +

      Trainerroad, One Year In

      +
      +: + Last year I interviewed Sika Henry, who I pretty much forced to use TR for 6 weeks. Here's my 1-year checkup. (Isn't that a truer test?) +
      +
      +
      +
      +
      + +
      +
      +

      Gettin' Groovy with Rouvy - Part 1: Getting Started

      +
      +: + Stationary is the hottest segment in non-motorized cycling. Rouvy is yet another stationary brand you should know about. +
      +
      +
      +
      +
      + +
      +
      +

      Zwift: A Potent Tool for Training

      +
      +: + Virtual racing is Blah, Blah, Blah for many of you. You’re just interested in Zwift’s workout mode. So let’s get to it. +
      +
      +
      +
      +
      + +
      +
      +

      Trainerroad's Workout Calendar

      +
      +: + It's for triathletes and cyclists who want a powerful workout organizer, but not the ardency required of the Training Peaks user. +
      +
      +
      +
      +
      + +
      +
      +

      Zwift: Let's Dance!

      +
      +: + What about the social aspect of riding a bike? Zwift is or can be (among other things) a Massively Multiplayer Online Game. +
      +
      +
      +
      +
      + +
      +
      +

      CycleOps H2, Fifteen Rides In

      +
      +: + After dozens of hours on both CycleOps Hammer and H2, here's my long term review, with an emphasis on the new H2. +
      +
      +
      +
      +
      + +
      +
      +

      It's Game On With Zwift

      +
      +: + Zwift is a game. Or a training tool. It's also a virtual community, like Westworld, except there's real people behind every avatar. +
      +
      +
      +
      +
      + +
      +
      +

      Zwift: The Deeper Dive

      +
      +: + Zwift is not hard to join; not hard to commence; but it’ll take you a good while to plumb its depths. Let's plumb its (shallow) depths. +
      +
      +
      +
      +
      + +
      +
      +

      Zwift: Welcome to Watopia

      +
      +: + Let’s get Zwift installed and configured using virtual power, so you can ride in the Zwift virtual world easily and without big expense. +
      +
      +
      +
      +
      +
      +
      Prev Next
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Indoor_Training/more5.html b/site/slowtwitch.com/www/Indoor_Training/more5.html new file mode 100644 index 0000000..0dae259 --- /dev/null +++ b/site/slowtwitch.com/www/Indoor_Training/more5.html @@ -0,0 +1,850 @@ + + + +<:: Welcome to Slowtwitch.com ::> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +
      +

      Indoor Training

      +
      +
      Prev Next
      +
      +
      +
      +
      + +
      +
      +

      Feelin' Rouvy

      +
      +: + Rouvy is about to join Zwift and others by offering a multi-rider experience +
      +
      +
      +
      +
      + +
      +
      +

      Revised, Expanded Zwift Rides

      +
      +: + We changing one of our Slowtwitch Zwift rides and adding a third. +
      +
      +
      +
      +
      + +
      +
      +

      Slowtwitch's Pinball Wizard

      +
      +: + Lionel Sanders was trading punches with... Matt Gardiner? He's a full time CPA, not a bike racer, but sure plays a mean pinball. +
      +
      +
      +
      +
      + +
      +
      +

      Zwift Foibles

      +
      +: + I am sort of computer dyslexic. If you’re like me what I write below might resonate and make your Zwifting a little easier to wrangle. +
      +
      +
      +
      +
      + +
      +
      +

      How Does the Latest KICKR Compare?

      +
      +: + Slowtwitch just announced its second weekly Zwift event. How about a smart trainer? After several months on the KICKR 2017, here's our view. +
      +
      +
      +
      +
      + +
      +
      +

      It's the Hilly Vanilli, SlowZwifters 2nd Weekly Ride

      +
      +: + If you build it they will Zwift. Slowtwitch weekly ride #2 will take place on Thursdays, and this is a little different than the Tuesday weekly event. +
      +
      +
      +
      +
      + +
      +
      +

      Slowtwitchers Take On Sufferlandria

      +
      +: + At least 14 Slowtwitchers completed the week long 799 minutes of the 6th annual Tour of Sufferlandria. +
      +
      +
      +
      +
      + +
      +
      +

      How a Woodway treadmill became a reality

      +
      +: + Our editor-at-large Herbert Krabel had been lusting for a Woodway treadmill ever since he experienced one of them at the NY Giants training center, but the $12,000 price tag was a massive hurdle. +
      +
      +
      +
      +
      + +
      +
      +

      The Jewel in Racermate's Crown Up for Sale

      +
      +: + Computrainer is gone, the marine and aviation products are gone or soon will be, and Velotron is now on the block. Interested? +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      + +
      +
      +

      Turbo Trainers and Rollers

      +
      +: + All dumb trainers needed was a little remedial help. Now you can Zwift, TrainerRoad, PerfPro, FulGaz, for cheap, cheap, cheap. +
      +
      +
      +
      +
      + +
      +
      +

      Elite Direto

      +
      +: + It may not the most feature-rich, nor the quietest, nor perhaps the smoothest, but it is certainly a bang-for- buck smart trainer finalist. +
      +
      +
      +
      +
      + +
      +
      +

      Cobb's Move Into Stationary

      +
      +: + This company, best known for its saddles, is either lucky or smart or both, because it's added stationary to its offerings. +
      +
      +
      +
      +
      + +
      +
      +

      CycleOps Hammer: Long Term Review

      +
      +: + Stable and sturdy. Great price at $1199. Power accuracy not on par with some direct-drive smart trainers. Smooth road feel compensates. +
      +
      +
      +
      +
      + +
      +
      +

      A Newbie Tackles TrainerRoad

      +
      +: + Clamping my bike up to a CycleOps Fluid 2 Trainer, pedaling while dying of boredom, was my indoor riding experience. +
      +
      +
      +
      +
      + +
      +
      +

      KISS Super League: Dawn of a New Era?

      +
      +: + We are witnesses to the beginning of a new era: Professional eCycling has emerged with the introduction of the KISS Super League. Where can it go from here? +
      +
      +
      +
      +
      + +
      +
      +

      Tacx NEO v Wahoo Kickr

      +
      +: + The NEO is a third more money, bulkier, hard to move. It is also more stable, more roadlike, more sensitive to virtual road surfaces. +
      +
      +
      +
      +
      + +
      +
      +

      CycleOps' Direct Drive Trainer

      +
      +: + Already the pervasive brand in stationary bike trainers, The Hammer takes dead aim at the growing smart trainer market. +
      +
      +
      +
      +
      + +
      +
      +

      The Treadmill Revolution?

      +
      +: + Stationary training on Slowtwitch is almost all about the bike, not the run. But 72 percent of you run on treadmills. Why is there no treadmill ecosystem? +
      +
      +
      +
      +
      + +
      +
      +

      Smart Trainers

      +
      +: + Unlike children where a precocious tendency might be a sign of intellect, there is no value in a sassy trainer. +
      +
      +
      +
      +
      + +
      +
      +

      Rollers

      +
      +: + This month 3 in 10 Slowtwitchers said their year-end spend will be on stationary training, equal to all other sport spending combined. Here is one class of stationary trainer. +
      +
      +
      +
      +
      + +
      +
      +

      Turbo Trainers

      +
      +: + The ubiquitous product for indoor cycling. Attaches to the rear of the bike, suspends it just above the ground, exerts some form of resistance while you pedal. +
      +
      +
      +
      +
      +
      +
      Prev Next
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      + + + + + + + + + + diff --git a/site/slowtwitch.com/www/New/2017-11-13.html b/site/slowtwitch.com/www/New/2017-11-13.html new file mode 100644 index 0000000..548a20e --- /dev/null +++ b/site/slowtwitch.com/www/New/2017-11-13.html @@ -0,0 +1,574 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Monday, November 13 2017
      +

      New Links

      +Indoor Training +
      +
      + +
      +
      +

      Zwift: The Deeper Dive

      +
      +: + Zwift is not hard to join; not hard to commence; but it’ll take you a good while to plumb its depths. Let's plumb its (shallow) depths. +
      +
      +
      +News +
      +
      + +
      +
      +

      We Noticed: Oakley, Noxgear, DaHänger and Wattie Ink

      +
      +: + Four products caught the attention of Herbert Krabel and he explains within this article why he noticed them. The Oakley ARO7 helmet, the Noxgear Tracer360 vest, the DaHänger Dan bike mount and the ... +
      +
      +
      +Tech +
      +
      + +
      +
      +

      Repurposing The Vasa Swim Ergometer

      +
      +: + Winter Sports Editor Jon Schafer explains how to repurpose the Vasa Swim Ergometer into a erg for Nordic skiing and more. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2017-11-16.html b/site/slowtwitch.com/www/New/2017-11-16.html new file mode 100644 index 0000000..c701e5b --- /dev/null +++ b/site/slowtwitch.com/www/New/2017-11-16.html @@ -0,0 +1,548 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Thursday, November 16 2017
      +

      New Links

      +Tech +
      +
      + +
      +
      +

      Hands On With The Zwim HUD

      +
      +: + We got our first look at a working prototype of the Zwim HUD, currently seeking funding via IndieGoGo. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2017-11-17.html b/site/slowtwitch.com/www/New/2017-11-17.html new file mode 100644 index 0000000..128343d --- /dev/null +++ b/site/slowtwitch.com/www/New/2017-11-17.html @@ -0,0 +1,548 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Friday, November 17 2017
      +

      New Links

      +News +
      +
      + +
      +
      +

      Ben Kanute, Flora Duffy lead Day 1 - Island House Tri

      +
      +: + Ben Kanute of the U.S. and Flora Duffy of Bermuda led Day One of the Island House Triathlon. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2017-11-18.html b/site/slowtwitch.com/www/New/2017-11-18.html new file mode 100644 index 0000000..457bb19 --- /dev/null +++ b/site/slowtwitch.com/www/New/2017-11-18.html @@ -0,0 +1,560 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Saturday, November 18 2017
      +

      New Links

      +News +
      +
      + +
      +
      +

      Kanute, Duffy win Island House Overall titles

      +
      +: + Ben Kanute edged Terenzo Bozzone by four-tenths of a second, Flora Duffy won by 1:14 at Island House. +
      +
      +
      +
      +
      + +
      +
      +

      Raelert, Watkinson win 24th Laguna Phuket Triathlon

      +
      +: + Michael Raelert won his 3rd straight and Amelia Watkinson won her 2nd in a row at Laguna Phuket. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2017-11-19.html b/site/slowtwitch.com/www/New/2017-11-19.html new file mode 100644 index 0000000..a75a7a1 --- /dev/null +++ b/site/slowtwitch.com/www/New/2017-11-19.html @@ -0,0 +1,548 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Sunday, November 19 2017
      +

      New Links

      +News +
      +
      + +
      +
      +

      Sali, Sanders take Ironman Arizona

      +
      +: + Kaisa Sali comes from behind and Lionel Sanders three-peats, to earn the elite titles at Ironman Arizona. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2017-11-20.html b/site/slowtwitch.com/www/New/2017-11-20.html new file mode 100644 index 0000000..8bfc696 --- /dev/null +++ b/site/slowtwitch.com/www/New/2017-11-20.html @@ -0,0 +1,548 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Monday, November 20 2017
      +

      New Links

      +Interview +
      +
      + +
      +
      +

      Chasing Ritch Viola of Every Man Jack

      +
      +: + Ritch Viola is the creator of the Every Man Jack brand and after finishing second in his age group in Kona he finally agreed to an interview. This pursuit of Viola actually started more than three 3 ... +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2018-01-07.html b/site/slowtwitch.com/www/New/2018-01-07.html new file mode 100644 index 0000000..d12c381 --- /dev/null +++ b/site/slowtwitch.com/www/New/2018-01-07.html @@ -0,0 +1,576 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Sunday, January 07 2018
      +

      New Links

      +Features +
      +
      + +
      +
      +

      TC’s favorite tri pics of 2017

      +
      +: + For your consideration, Timothy Carlson presents his some of his favorite triathlon photographs of the 2017 season. +
      +
      +
      +Training > Beginners +
      +
      + +
      +
      +

      Guppy Challenge: Week 5

      +
      +: + Are you able to perform one-arm pulls as described? If so, you are swimming hip-driven freestyle! Now let's begin to focus on how you pull. +
      +
      +
      +Training > Swimming +
      +
      + +
      +
      +

      Thoughts on Swimming

      +
      +: + Slowtwitch is lucky to have superswimmers in the community. One is Tim Liebhold, and he shares his tips for Adult Onset Swimmers +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2018-01-08.html b/site/slowtwitch.com/www/New/2018-01-08.html new file mode 100644 index 0000000..14a5f7b --- /dev/null +++ b/site/slowtwitch.com/www/New/2018-01-08.html @@ -0,0 +1,575 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Monday, January 08 2018
      +

      New Links

      +Interview +
      +
      + +
      +
      +

      A Heart in Two Worlds

      +
      +: + For the Butterfields, John Denver is on the iPod. Horses, llamas, goats, chickens, guinea fowl, and a peacock. But Bermuda is not forgotten. +
      +
      +
      +
      +
      + +
      +
      +

      Halo free, but a long way to go

      +
      +: + A terrible accident a few days before the 2017 IRONMAN World Championships took Brit Tim Don out of the race. The Halo finally came off, but he is still a long way from being fully recovered. +
      +
      +
      +Opinion +
      +
      + +
      +
      +

      Is Triathlon Up or Down for 2018?

      +
      +: + For those who think anything over 300 word is tl;dr: It'll be up in the aggregate, but the median race will be flat or down. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2018-01-11.html b/site/slowtwitch.com/www/New/2018-01-11.html new file mode 100644 index 0000000..4f29fc8 --- /dev/null +++ b/site/slowtwitch.com/www/New/2018-01-11.html @@ -0,0 +1,563 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Thursday, January 11 2018
      +

      New Links

      +Features +
      +
      + +
      +
      +

      Raising Kain

      +
      +: + Jeremy Kain ran a world best mile for a 12-year-old last summer, carefully nurtured by his talented parents. +
      +
      +
      +Lifestyle +
      +
      + +
      +
      +

      Under My Wing Triathlon Summit

      +
      +: + 16 high achieving college triathletes from around the US take part in an intensive triathlon summit produced by Angela Naeth and Red Bull. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2018-01-12.html b/site/slowtwitch.com/www/New/2018-01-12.html new file mode 100644 index 0000000..b1e5cc8 --- /dev/null +++ b/site/slowtwitch.com/www/New/2018-01-12.html @@ -0,0 +1,550 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Friday, January 12 2018
      +

      New Links

      +Interview +
      +
      + +
      +
      +

      Life After - Chris Legh

      +
      +: + Aussie Chris Legh won 2 IRONMAN titles but is likely best remembered for his scary collapse 50 meters before the finish in Kona. But this retired professional will soon be known as race director ... +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2018-01-14.html b/site/slowtwitch.com/www/New/2018-01-14.html new file mode 100644 index 0000000..3f632cd --- /dev/null +++ b/site/slowtwitch.com/www/New/2018-01-14.html @@ -0,0 +1,550 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Sunday, January 14 2018
      +

      New Links

      +News +
      +
      + +
      +
      +

      Lionel Sanders and Barbara Riveros win Pucon 70.3

      +
      +: + Lionel Sanders repeated as men’s champion and Barbara Riveros took her 4th women’s title at Ironman 70.3 Pucon. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2018-04-21.html b/site/slowtwitch.com/www/New/2018-04-21.html new file mode 100644 index 0000000..e5218f7 --- /dev/null +++ b/site/slowtwitch.com/www/New/2018-04-21.html @@ -0,0 +1,562 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Saturday, April 21 2018
      +

      New Links

      +News +
      +
      + +
      +
      +

      Gonzalez, Van Vlerken win Gran Canaria

      +
      +: + Pablo Dapena Gonzalez upsets Bocherer and Lange; Van Vlerken dominates Challenge Mogan Gran Canaria. +
      +
      +
      +
      +
      + +
      +
      +

      Luxford, Phillips take Challenge Melbourne

      +
      +: + Annabel Luxford won it wire-to-wire; Mike Phillips outran Levi Maxwell to prevail at Challenge Melbourne. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2018-04-22.html b/site/slowtwitch.com/www/New/2018-04-22.html new file mode 100644 index 0000000..e045215 --- /dev/null +++ b/site/slowtwitch.com/www/New/2018-04-22.html @@ -0,0 +1,574 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Sunday, April 22 2018
      +

      New Links

      +News +
      +
      + +
      +
      +

      Piampiano, Viennot prevail at Peru 70.3

      +
      +: + Sarah Piampiano edged Kirsty Jahn and Cyril Viennot topped Andy Potts at IM 70.3 Peru. +
      +
      +
      +
      +
      + +
      +
      +

      Amorelli, Oliveira take home country wins at Florianopolis

      +
      +: + Igor Amorelli out biked Tim O’Donnell and Pamella Oliveira edged Bruna Mahn by 11 seconds at Florianopolis 70.3. +
      +
      +
      +
      +
      + +
      +
      +

      Jason West, Sophie Chase win Surf City Escape

      +
      +: + West comes from behind to win by 8 seconds; Chase runs down Haskins to win Surf City Escape Tri. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2018-04-23.html b/site/slowtwitch.com/www/New/2018-04-23.html new file mode 100644 index 0000000..a00a4bb --- /dev/null +++ b/site/slowtwitch.com/www/New/2018-04-23.html @@ -0,0 +1,562 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Monday, April 23 2018
      +

      New Links

      +Interview +
      +
      + +
      +
      +

      Bermuda Chronicles: A Taylor-Made Course

      +
      +: + This champion surf swimmer took to the bike like a pro, and is getting better on the run. America's Taylor Spivey could impress in Bermuda. +
      +
      +
      +
      +
      + +
      +
      +

      Bermuda's Butterfield is no One-Trick Pony

      +
      +: + Butterfield made the most of his time at the Commonwealth Games on Australia's Gold Coast racing both triathlon and marathon. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2018-04-24.html b/site/slowtwitch.com/www/New/2018-04-24.html new file mode 100644 index 0000000..409f633 --- /dev/null +++ b/site/slowtwitch.com/www/New/2018-04-24.html @@ -0,0 +1,550 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Tuesday, April 24 2018
      +

      New Links

      +Interview +
      +
      + +
      +
      +

      The very busy and happy Ryan Linden

      +
      +: + Ryan Linden of Team Every Man Jack has been very busy already even though his season has not yet started. He just returned from Boston where his wife Des Linden grabbed a magnificent win at the ... +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2018-04-26.html b/site/slowtwitch.com/www/New/2018-04-26.html new file mode 100644 index 0000000..8bc2425 --- /dev/null +++ b/site/slowtwitch.com/www/New/2018-04-26.html @@ -0,0 +1,550 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Thursday, April 26 2018
      +

      New Links

      +Products > Tri Bike by brand +
      +
      + +
      +
      +

      Diaa Nour's unique Ventum One

      +
      +: + Ventum co-owner Diaa Nour is originally from Egypt and he wanted to have a bike that is a tribute to his home country. Currently at IRONMAN Texas this bike with very intricate golden details is on ... +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2018-04-27.html b/site/slowtwitch.com/www/New/2018-04-27.html new file mode 100644 index 0000000..c4d7f92 --- /dev/null +++ b/site/slowtwitch.com/www/New/2018-04-27.html @@ -0,0 +1,563 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Friday, April 27 2018
      +

      New Links

      +Products > Tri Bike by brand > Specialized +
      +
      + +
      +
      +

      The Bermuda themed Specialized Tarmac of Flora Duffy

      +
      +: + Flora Duffy will start the 2018 ITU World Triathlon Bermuda aboard a very beautiful custom painted Specialized Tarmac and we managed to take a closer look. +
      +
      +
      +Tech +
      +
      + +
      +
      +

      What’s the Best Tire Size for Me in 2018?

      +
      +: + We look at tire sizes for 2018. What should you use and why? How have tire sizes evolved? +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2018-04-28.html b/site/slowtwitch.com/www/New/2018-04-28.html new file mode 100644 index 0000000..afa1f73 --- /dev/null +++ b/site/slowtwitch.com/www/New/2018-04-28.html @@ -0,0 +1,550 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Saturday, April 28 2018
      +

      New Links

      +News +
      +
      + +
      +
      +

      Hanson, Hauschildt lead Texas speed fest

      +
      +: + Matt Hanson and Melissa Hauschildt break Ironman brand records at The Woodlands. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2018-06-06.html b/site/slowtwitch.com/www/New/2018-06-06.html new file mode 100644 index 0000000..b12f58c --- /dev/null +++ b/site/slowtwitch.com/www/New/2018-06-06.html @@ -0,0 +1,597 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Wednesday, June 06 2018
      +

      New Links

      +News +
      +
      + +
      +
      +

      Breaking Down Kanute's Winning Escape

      +
      +: + A deep dive into Ben Kanute's winning effort at Escape From Alcatraz. +
      +
      +
      +Products > Components +
      +
      + +
      +
      +

      Shimano Synchro for Triathlon

      +
      +: + Lefty-uppy, righty-downy. That’s 1-button/Synchro from Shimano. It's very possibly what's coming on your next bike. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2018-06-07.html b/site/slowtwitch.com/www/New/2018-06-07.html new file mode 100644 index 0000000..187c1ce --- /dev/null +++ b/site/slowtwitch.com/www/New/2018-06-07.html @@ -0,0 +1,584 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Thursday, June 07 2018
      +

      New Links

      +Tech +
      +
      + +
      +
      +

      The Frame Material Debate

      +
      +: + We investigate the debate of frame materials, and whether there is any future for materials other than carbon fiber. Can steel survive? +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2018-06-10.html b/site/slowtwitch.com/www/New/2018-06-10.html new file mode 100644 index 0000000..1ff0b74 --- /dev/null +++ b/site/slowtwitch.com/www/New/2018-06-10.html @@ -0,0 +1,656 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Sunday, June 10 2018
      +

      New Links

      +News +
      +
      + +
      +
      +

      Canadians rule Eagleman

      +
      +: + Cody Beals and Stephanie Roy win one for the Maple Leaf crowd at the classic Eagleman half. +
      +
      +
      +
      +
      + +
      +
      +

      Currie, Adam outrace headliners at Cairns

      +
      +: + Braden Currie outran Javier Gomez and Teresa Adam outswam Mirinda Carfrae to win IM Cairns. +
      +
      +
      +
      +
      + +
      +
      +

      Jahn, Leiferman win Ironman Boulder

      +
      +: + Kirsty Jahn of Canada and Chris Leiferman of the U.S. win Ironman Boulder when it’s 93 in the shade. +
      +
      +
      +
      +
      + +
      +
      +

      Richard Murray victorious at Leeds

      +
      +: + Topping all his other successes, Richard Murray finally takes his first WTS victory at Leeds. +
      +
      +
      +
      +
      + +
      +
      +

      Ryf, Amberger take Rapperswil-Jona

      +
      +: + Daniela Ryf crushed the women’s field, Josh Amberger edged Manuel Küng at Ironman 70.3 Switzerland. +
      +
      +
      +
      +
      + +
      +
      +

      Smales, Pallant top Staffordshire 70.3

      +
      +: + Great Britons Elliot Smales and Emma Pallant prevail at Ironman 70.3 Staffordshire. +
      +
      +
      +
      +
      + +
      +
      +

      Vicky Holland tops women at WTS Leeds

      +
      +: + Vicky Holland wins her first WTS race in three years while Flora Duffy sits out due to lingering foot injury. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2018-06-11.html b/site/slowtwitch.com/www/New/2018-06-11.html new file mode 100644 index 0000000..ece0b66 --- /dev/null +++ b/site/slowtwitch.com/www/New/2018-06-11.html @@ -0,0 +1,584 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Monday, June 11 2018
      +

      New Links

      +Products > Components +
      +
      + +
      +
      +

      Do You Need This Feature?

      +
      +: + I find I need very little float in my cleat. But I’m very picky where that float starts and stops. It's one of several critical pedal features. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2018-06-13.html b/site/slowtwitch.com/www/New/2018-06-13.html new file mode 100644 index 0000000..568771e --- /dev/null +++ b/site/slowtwitch.com/www/New/2018-06-13.html @@ -0,0 +1,584 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Wednesday, June 13 2018
      +

      New Links

      +Products +
      +
      + +
      +
      +

      The Serios of Matt Russell

      +
      +: + Matt Russell recently brought his Diamondback Serios to the A2 Wind Tunnel in Mooresville, NC and we took a closer look at this race ready machine. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2020-02-29.html b/site/slowtwitch.com/www/New/2020-02-29.html new file mode 100644 index 0000000..e09c1ad --- /dev/null +++ b/site/slowtwitch.com/www/New/2020-02-29.html @@ -0,0 +1,574 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Saturday, February 29 2020
      +

      New Links

      +News +
      +
      + +
      +
      +

      Rupp, Tuliamuk Win U.S. Marathon Trials

      +
      +: + The hills of Atlanta bring some surprising faces to the 2020 U.S. Olympic Marathon team. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2020-03-01.html b/site/slowtwitch.com/www/New/2020-03-01.html new file mode 100644 index 0000000..8316769 --- /dev/null +++ b/site/slowtwitch.com/www/New/2020-03-01.html @@ -0,0 +1,574 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Sunday, March 01 2020
      +

      New Links

      +News +
      +
      + +
      +
      +

      Scenes From Atlanta: 2020 U.S. Olympic Marathon Trials

      +
      +: + The unforgiving course and challenging conditions pay off with excellent racing and stunning heartbreak. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2020-03-02.html b/site/slowtwitch.com/www/New/2020-03-02.html new file mode 100644 index 0000000..50b3c8f --- /dev/null +++ b/site/slowtwitch.com/www/New/2020-03-02.html @@ -0,0 +1,587 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Monday, March 02 2020
      +

      New Links

      +Indoor Training +
      +
      + +
      +
      +

      This Week on Slowtwitch Indoors: Return to Watopia

      +
      +: + All of our rides and runs take place in Zwift's original virtual world. +
      +
      +
      +News +
      +
      + +
      +
      +

      Impressive efforts at ÖtillÖ Swimrun Catalina

      +
      +: + The inaugural ÖtillÖ Swimrun Catalina proved to be a tough test both for strong seasoned teams as well as new ambitious teams, and here are some stunning impressions from that day. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2020-03-05.html b/site/slowtwitch.com/www/New/2020-03-05.html new file mode 100644 index 0000000..67e97b1 --- /dev/null +++ b/site/slowtwitch.com/www/New/2020-03-05.html @@ -0,0 +1,574 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Thursday, March 05 2020
      +

      New Links

      +News +
      +
      + +
      +
      +

      NormaTec Acquired By Hyperice, Inc.

      +
      +: + The compression system innovator is acquired by the makers of percussion, vibration, and thermal technology. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2020-08-21.html b/site/slowtwitch.com/www/New/2020-08-21.html new file mode 100644 index 0000000..525ef82 --- /dev/null +++ b/site/slowtwitch.com/www/New/2020-08-21.html @@ -0,0 +1,580 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Friday, August 21 2020
      +

      New Links

      +Training > General Physiology +
      +
      + +
      +
      +

      INSCYD the Numbers: FTP Part II

      +
      +: + This week, we take a deeper dive into this classic cycling metric. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2020-08-24.html b/site/slowtwitch.com/www/New/2020-08-24.html new file mode 100644 index 0000000..85bb653 --- /dev/null +++ b/site/slowtwitch.com/www/New/2020-08-24.html @@ -0,0 +1,593 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Monday, August 24 2020
      +

      New Links

      +Gravel +
      +
      + +
      +
      +

      First Crack at a Genuine Bikepacker

      +
      +: + It's quite worthy as a standalone gravel racer, but my primary reason for the OBED Boundary build was as a bikepacker. +
      +
      +
      +Indoor Training +
      +
      + +
      +
      +

      Ladies of Zwift: the Unique Experiences of Women’s Cycling in a Virtual World

      +
      +: + Zwift has created an outstanding virtual world that still has significant barriers for its female riders. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2020-08-25.html b/site/slowtwitch.com/www/New/2020-08-25.html new file mode 100644 index 0000000..dc63f59 --- /dev/null +++ b/site/slowtwitch.com/www/New/2020-08-25.html @@ -0,0 +1,580 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Tuesday, August 25 2020
      +

      New Links

      +Interview +
      +
      + +
      +
      +

      The Man Who Allows You to See

      +
      +: + French ultra runner Sébastien Chaigneau retired as a pro but he is still running up front during races such as UTMB - but now with a video camera to let all of us see the action up close. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2020-08-27.html b/site/slowtwitch.com/www/New/2020-08-27.html new file mode 100644 index 0000000..ea3c52f --- /dev/null +++ b/site/slowtwitch.com/www/New/2020-08-27.html @@ -0,0 +1,580 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Thursday, August 27 2020
      +

      New Links

      +Lifestyle +
      +
      + +
      +
      +

      Sarah True Explores the Frontier

      +
      +: + True has continually re-invented herself as an athlete. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2020-08-28.html b/site/slowtwitch.com/www/New/2020-08-28.html new file mode 100644 index 0000000..56cbf60 --- /dev/null +++ b/site/slowtwitch.com/www/New/2020-08-28.html @@ -0,0 +1,580 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Friday, August 28 2020
      +

      New Links

      +Interview +
      +
      + +
      +
      +

      When it Rains, it Often Pours

      +
      +: + Charlotte-based professional triathlete Kelly Fillnow crashed on a cruiser bike while spectating and supporting athletes at 70.3 North Carolina and that meant the end of her season. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2022-10-10.html b/site/slowtwitch.com/www/New/2022-10-10.html new file mode 100644 index 0000000..c911be7 --- /dev/null +++ b/site/slowtwitch.com/www/New/2022-10-10.html @@ -0,0 +1,492 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + + +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Monday, October 10 2022
      +

      New Links

      +Indoor Training +
      +
      + +
      +
      +

      Watts Up in Watopia: ZA Tri at Kona

      +
      +: + Congratulations to Zwift Academy Tri +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2022-10-14.html b/site/slowtwitch.com/www/New/2022-10-14.html new file mode 100644 index 0000000..86d15e4 --- /dev/null +++ b/site/slowtwitch.com/www/New/2022-10-14.html @@ -0,0 +1,505 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + + +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Friday, October 14 2022
      +

      New Links

      +Bike Fit > General Fit Articles +
      +
      + +
      +
      +

      Bike Positions of the Male Pros in Kona

      +
      +: + Here's a sampling of male pros rode their bikes in Kona. The consensus position changed, but not profoundly so. +
      +
      +
      +Products > Things that Roll > Tires +
      +
      + +
      +
      +

      Tires the Pros Rode in Kona

      +
      +: + An overview of the brands, models, tube type or tubeless, widths of tires ridden by the pros in Kona. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2022-10-15.html b/site/slowtwitch.com/www/New/2022-10-15.html new file mode 100644 index 0000000..8bf899a --- /dev/null +++ b/site/slowtwitch.com/www/New/2022-10-15.html @@ -0,0 +1,529 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + + +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Saturday, October 15 2022
      +

      New Links

      +Bike Fit > General Fit Articles +
      +
      + +
      +
      +

      Pro Women's Bike Positions in Kona

      +
      +: + Women’s and men's bike positions in Kona looked almost the same. With one difference. +
      +
      +
      +News +
      +
      + +
      +
      +

      Koolhaas, Bernardi Prevail at 70.3 Cascais

      +
      +: + Menno Koolhaas edged Mathis Margirier by 15 seconds; Marta Bernardi outruns Nikki Bartlett by 1:25 at Cascais. +
      +
      +
      +
      +
      + +
      +
      +

      McElroy, Merle Victorious at Tongyeong World Cup

      +
      +: + McElroy unleashed race-best 30:56 run to win; Merle won her first World Cup by 23 seconds over Annika Koch. +
      +
      +
      +
      +
      + +
      +
      +

      Returning Simmonds and Newcomer Wayaffe Win Hilly Challenge Mallorca

      +
      +: + Imogen Simmonds and Jonathan Wayaffe reigned victorious during a hot and hilly Challenge Peguera-Mallorca race on 15 October. +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2022-10-16.html b/site/slowtwitch.com/www/New/2022-10-16.html new file mode 100644 index 0000000..ad63d16 --- /dev/null +++ b/site/slowtwitch.com/www/New/2022-10-16.html @@ -0,0 +1,505 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + + +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Sunday, October 16 2022
      +

      New Links

      +Features +
      +
      + +
      +
      +

      Bike Brands and Sponsored Pros in Kona

      +
      +: + Which brands are over- and under-invested in pro long course athletes? +
      +
      +
      +News +
      +
      + +
      +
      +

      Foley, Ueda Tops at Waco 70.3

      +
      +: + Foley topped Justin Metzler by 3:17 and Ai Ueda dominated women’s field, 5:56 over runner-up Sarah Bishop, +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/2022-10-17.html b/site/slowtwitch.com/www/New/2022-10-17.html new file mode 100644 index 0000000..2be5d79 --- /dev/null +++ b/site/slowtwitch.com/www/New/2022-10-17.html @@ -0,0 +1,492 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + + +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > New > Monday, October 17 2022
      +

      New Links

      +Indoor Training +
      +
      + +
      +
      +

      Watts Up in Watopia: LA Bicycle Academy

      +
      +: + Zwift provides support youth cycling program in Los Angeles +
      +
      +
      +
      +
      +
      +
      + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/New/index.html b/site/slowtwitch.com/www/New/index.html new file mode 100644 index 0000000..69cd178 --- /dev/null +++ b/site/slowtwitch.com/www/New/index.html @@ -0,0 +1,486 @@ + + + +<:: Welcome to Slowtwitch.com ::>: New Links + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + + +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + + + +
      +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/Ratings/index.html b/site/slowtwitch.com/www/Ratings/index.html new file mode 100644 index 0000000..2324a7b --- /dev/null +++ b/site/slowtwitch.com/www/Ratings/index.html @@ -0,0 +1,495 @@ + + + +<:: Welcome to Slowtwitch.com ::>: Top Rated Links + + + + + + + + + + + + + + + + + + + + + + +
      +
      + + + + +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +
      +
      +
      Home > Top Rated Links
      +

      Top Rated Links

      +

      Top 10 by rating (10 or more votes)

      + + + + + + +
      RatingVotesLink
      +

      Top 10 by votes (10 or more votes)

      + + + + + + +
      RatingVotesLink
      +
      +
      +
      +
      + +
      +
      + +
      + + diff --git a/site/slowtwitch.com/www/Travel/B_W_Intl_Bike_Box_II_7422.html b/site/slowtwitch.com/www/Travel/B_W_Intl_Bike_Box_II_7422.html new file mode 100644 index 0000000..3b27abf --- /dev/null +++ b/site/slowtwitch.com/www/Travel/B_W_Intl_Bike_Box_II_7422.html @@ -0,0 +1,657 @@ + + + + +B&W Intl Bike Box II - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      B&W Intl Bike Box II

      + +
      +
      +
      +

      Over the years I’ve owned and used a lot of bike cases. I own a lot of them now. It’s just a function of how long I’ve been doing this. I’ve owned a lot of soft cases but I’ve gravitated toward hard shell cases because of the protection offered and because most of the airline rules today say that a hard shell case is required to keep the airline from tagging the item as “fragile.”

      +

      According to B&W, this isn’t the case I should be showing you all. This one, the Bike Box II, is its best selling model. But it’s not what triathletes buy. Because B&W is a German company and has a close relationship with Canyon, I suspect its opinions are somewhat colored by Canyon’s exhortation against not removing the pursuit bar on its Speedmax SLX (the shipping of which is discussed in this Reader Forum thread). But I wrote B&W back with pictures of a P-Series Cervelo I’d just built slotting pretty easily into this case. I’m going to write about the case in the context of tri bike travel.

      Why do I like this case? First, it costs $299 ($265 on Amazon as of today). Do I have your attention? Second, it’s rather smallish; and third, the case weighs about 23 pounds, depending on what all you count in the weight. We’ll get to why those last 2 points are important.

      +

      The Bike Box II is called a “box” instead of a “case” because, I guess, it’s less substantial than their cases. But there’s nothing insubstantial about it. It’s a rugged, hard shell case. But it is unique, I’ll say that. It’s got 2 robust wheels on the back and 2 steering wheels on castors in the front (or the other way around, the castors in the back, which is my preference). The case is made of a pair of disconnected halves. It only becomes an actual case when assembled. Of course you could say that about, say, a Tri-All-3, but that case (a fave of mine) has a separate top and bottom. This case has a left and right.

      +

      The wheels need to be assembled on the case, if you were to buy it unassembled via mail order. The whole assembly process took me about 20 or 30 minutes. Here are the parts and the tools I used, though an electric tool is unnecessary.

      +

      The case comes with a bunch of protective aids which I believe (if I recall correctly) I’ve seen on Canyon’s bikes when they arrive in boxes. These are handy foam blocks with Velcro-like closures that will protect the tubes from contact.

      This case also comes with a pair of wheel bags. I have a lot of wheel bags and thankfully these bags are made slightly larger than my others, so that they nicely accept a 700c wheels with inflated gravel tires! There are also 3 foam pieces, layers, like lasagna layers, and you put the “filling” in between the layers. Finally, ther’es a hard, like a Styrofoam, block inside of which sits your big chain ring. You’ll see this in the image below.

      +

      I hope you don’t mind if I share a few tips on how I pack bikes as I discuss packing this P-Series in this case. We’ve always had to use devices to keep the airlines (or Fedex or UPS) from smashing our fork ends or dropouts in compression. The P-Series is a thru axle bike, and I cut a couple of pieces to length out of a piece of 3/8” aluminum conduit, and when I take the wheels off I put the thru axles back into the frame and fork, thru the conduit. That protects the frame and it keeps my thru axles from getting lost.

      +

      I always take the RD off the bike when shipping. You can tape the RD to the frame if you want or wrap it in bubble wrap but, screw it, I’m lazy, I just leave it sitting there inside the rear triangle. I take it off because this is the most likely bad thing to happen in shipping: a bent derailleur hanger or dropout. Taking the RD off (5mm Allen Wrench in most cases) inoculates your bike against this problem.

      I promise you, your new disc brake bike came assembled to the dealer with a couple of shims inside the calipers. Ask your LBS to give you those, and shove them back inside the calipers when you travel. Otherwise you may find your calipers sort of closed up upon reassembly.

      +

      You may find that it’s inconvenient, when you take the stem out of the steerer, to put the top cap back on the fork and have that fork flopping all around inside the frame. I know I do. It offends some sense of appropriateness in me. So, I travel with a pair of 20mm headset spacers, and when I take the stem of the fork I put those spacers in. Stems are almost always about 40mm in height as they pass over the steerer, so any combo of 40mm of headset spacers does the trick.

      +

      Speaking of discs, you may want to remove the your rotor from the wheel. This is why Shimano’s Centerlock standard is so much better than 6-bolt. I wrote about that here, so, read there if you want a little more info on that.

      +

      There’s one layer of foam on the outside, against the shell. Then I put in a wheel and then the frame and a second layer of foam. I also put my handlebars – I didn’t need to disassemble any wires or cables when shipping this bike – on this “layer.” Then the other wheels and here’s a bag (not included) inside of which I put the seat post/saddle, and the front hydration bottle, and my pedals. Then the last layer of foam and I’m done. Ready to close it up.

      +

      Now for the genius of this case, and why it can be so strong while remaining so light. You might think that you simply place one side over the other. No. The two sides overlap several times. It’s very clear where they do overlap.

      There is a guide, on each shell, where it’s clear that one shell slides inside the other. That’ll get you started. From then on it’s pretty straightforward.

      +

      It took me one bike packing exercise to figure it all out. Now, when I use this case, I lay one shell flat on the ground, and it’s the shell with all the straps attached. Strap are laid out, extended out, like your snow angel arms. Once the bike is packet, the other shell goes atop, and the top shell slotting into the bottom shell’s guide.

      Once the top shell is on, appropriately overlapped at every interval, the straps from the bottom shell are affixed (with Velcro). Then it’s pretty much ready to roll and I place the Bike Box II upright on its wheels.

      +

      Once the top shell is on, appropriately overlapped at every interval, the straps from the bottom shell are affixed (with Velcro). Then it’s pretty much ready to roll and I place the Bike Box II upright on its wheels.

      +

      The last thing to do is close up a big strap that spans the entire case. There’s a pin, around which the clasp pivots, that you must insert into a receiver with which it mates. Then you flip the clasp over, it locks, you’re done.

      If you find the clasp not sufficiently tight, there’s a pin on the clasp that you can remove with a Phillips screw driver, and the placement of that pin in 1 of 4 holes makes strap yet tighter, if need be (I didn’t need to adjust it).

      Now, let’s talk about size and weight. My first use of this case was last month. I and the wife flew to Rome, gravel bikes inside, rolled our cases to the train station, took a couple of train rides to Florence, threw the cases into the back of a taxi, drove to the airport to pick up a rental car. Now came the moment of truth. The car was a Fiat Tipo. Go ahead, click that link, look at that car and tell me if it inspires you with confidence, if you’re traveling with a pair of bike cases.

      +

      As you see the cases slid right in (one was this Bike Box II, the other a B&W Bike Guard Curv). This case barely cracks 90” inches when you measure length + height + width, and the airlines variously require a case not to exceed either 115” or 126” or thereabouts using this measuring system.

      Second, this Cervelo P-Series, when loaded – and this was no small bike, it was a size L, or size 58cm – weighed 47.6lb. I had room for, and could’ve placed in there, my helmet and shoes. For this Italy trip I had all of that plus my floor pump and it came in at exactly 50lb. That’s the magic number.

      It isn’t that the airlines will fly your bike as checked luggage. It’s that it’ll fly as checked luggage if it doesn’t exceed 50lb. This is the tricky part. You may get an extra 20lb if you’re flying in a forward cabin. Or not. If you’re flying coach on an airline that grants you this new same-as-checked-bag benefit, 50lb is the max. This is where the weight of the case becomes paramount.

      The cases were extracted from the back of the Fiat Tipo in the Strada Bianche region of Chiante, and rolled over cobbles and gravel. Back into the car and to a Schwable tire launch in another part of Florence. They were packed in and out of the cases several times. Then, finally – long story of bad navigational principles – they were muscled over 4 bridges spanning Venice canals, placed on a bus, then the airlines lost both cases for 3 days before they were finally delivered home, cases intact, bikes fine.

      A couple of notes. First, the case B&W most sells to triathletes is the Bike Guard Curv, the other case we took to Tuscany. It sells for about $800. Great case. I’m just struggling to find the extra $500 in value over the case reviewed here. Frugal by nature, I just... can't... find a good reason to spend more than the $200-and-something this case costs.

      Second note: The overlapping of the shells might be a bridge too far for TSA. Mind, I didn’t even think about this on last month’s trip and the Bike Box II was disassembled, checked, and reassembled (properly) by TSA. However, you may want to ask TSA to check it in your presence at the point of departure. (I do this with every bike case, though I forgot to do this on this recent trip and all went okay anyway.)

      And finally, if you look at the bottom of our pages you’ll see B&W is a Slowtwitch Partner. I first saw the bike cases made by B&W at Interbike last year (which was the last year of Interbike). I admired the cases, spent a lot of time in this company’s trade show booth, but I argued against any sort of partnership with Slowtwitch because I just didn’t intend to write anything about these cases editorially. We just left it there.

      What changed? I sent an email back the day after I read about American Airlines’ change of posture on checked bikes. (Airlines don’t generally announce they’re making travel better, cheaper, more comfortable for us.) “Now I have a narrative,” I wrote, “and I expect to be writing a lot about travel.” They recommended I try their cases and they sent some out. I asked for the lesser expensive cases not because of the price, but because their light weight works in conjunction with these new bike rules.

      This is the first of a number of bike case reviews forthcoming, including the Trico Ironcase, Tri-All-3, and others. Here’s B&W International if you want to read more about the case, tho this company has a U.S. office and warehouse, so cases purchased will generally be shipped to you from the continent in which you live.

      Note that we have a new “superthread” on the Reader Forum, Ministry of Travel, which will be a curated thread designed to inform and answer any questions you all may have about anything related to travel (you or your bike) that relates to this sport we’re in.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Travel/Delta_Demystified_Yes_Bikes_Travel_as_Checked_Bags_7423.html b/site/slowtwitch.com/www/Travel/Delta_Demystified_Yes_Bikes_Travel_as_Checked_Bags_7423.html new file mode 100644 index 0000000..72dc95c --- /dev/null +++ b/site/slowtwitch.com/www/Travel/Delta_Demystified_Yes_Bikes_Travel_as_Checked_Bags_7423.html @@ -0,0 +1,644 @@ + + + + +Delta Demystified: Yes, Bikes Travel as Checked Bags - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Delta Demystified: Yes, Bikes Travel as Checked Bags

      + +
      +
      +
      +

      Delta Airlines announced a change in its airline rules that brought it to parity with American Airlines: Checked bikes will travel simply as a checked bag, no extra fee. We reported Delta’s near-conformity with American’s new rules.

      Were we premature? A thread sprung up on the Slowtwitch Reader Forum asking about this and, indeed, the language was confusing to some. While most readers are now reporting bikes-same-as-checked-bags, a few were charged $150 per trip and had to appeal to Delta after their travel for relief.

      +

      Baggage must be less than 62” (length + width + height) to be considered not oversized. Are bikes an exception to this? “Bicycles… are allowed as checked baggage on most flights and fees … apply.” Seems straightforward. “Bicycles weighing over 50 lbs. will be charged the applicable excess weight fee,” which is in keeping with American’s new rules.

      However, the policy goes on to say that when dimensions, “exceed 115 linear inches (292 cm) or exceeds 100 lbs item not accepted.” The policy as written does not explicitly say that bicycles between 62 inches and 115 inches travel as regular checked baggage, with no oversized fee applied.

      Delta Airlines has a department that handles questions from the media. I placed this question to them. "The 'Standard baggage allowance and fees… apply' argues that bicycles go as regular checked baggage," I wrote. "But the policies on the website are not clear about whether an item (bicycle in particular) between 62” and 115” are simply allowed on the plane (but subject to an oversize charge) or whether it travels as regular checked baggage. Are bicycles, under 50lb, hard shell case, charged as regular checked bags? Or are they subject to oversize fees?"

      The Delta media representative wrote back with the following: “There are no specialty fees for bicycles. Bicycles are charged as a standard checked bag, and standard baggage fees (including overweight and oversized fees) can still apply.”

      Nevertheless, ticket agents can be confused by language that isn’t 100 percent clear. Wrote one Slowtwitcher, “I flew at the end of July before I knew about this updated policy and it was a tale of two. Leaving Los Angeles flying to Michigan, was charged $150. Leaving Michigan for Los Angeles, was charged $0. So, I got home, found [the Forum thread referenced above] and called Delta to tell them about my $150 charge. They refunded my money and apologized. So, the policy does exist!!”

      The tricky part isn’t the size, it’s the weight. Read today’s review of B&W’s Bike Box II. With a tri bike (especially) the case itself places the travel in peril of exceeding 50lb, when the case itself weighs more than 25lb and many cases do. Weight of the empty case was never that much of a consideration to me when making a bike case purchase. It is now.

      Note there is a new "superthread" on our Reader Forum, as of today, Ministry of Travel, which will be a curated thread answering any questions you have about travel in the context of swim, bike and run (whatever "Travel" may grow to encompass).

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Travel/Straight_from_TSA_Best_Practices_on_Bike_Inspections_7430.html b/site/slowtwitch.com/www/Travel/Straight_from_TSA_Best_Practices_on_Bike_Inspections_7430.html new file mode 100644 index 0000000..4fb927e --- /dev/null +++ b/site/slowtwitch.com/www/Travel/Straight_from_TSA_Best_Practices_on_Bike_Inspections_7430.html @@ -0,0 +1,645 @@ + + + + +Straight from TSA: Best Practices on Bike Inspections - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Straight from TSA: Best Practices on Bike Inspections

      + +
      +
      +
      +

      We have an evergreen, curated superthread on our Reader Forum called Ministry of Travel. It’s a week old, it’s already busy, and it’s there because of American Airlines’ bike policy from earlier this summer, followed by Delta’s near match in its policy.

      When we ask you, 44 percent say you’ll bike will travel underneath you, in the plane’s belly, more often than before because of these new rules. More to the point, if you’re an airline, 1 in 3 of you say you’re likely to change carrier loyalties because of this new policy. And of those who do are loyal to a carrier who doesn’t have this new liberal bike policy (e.g., United as of this writing), 73 percent of you say you intend to change your loyalty. (Even if you clicked that radio button out of frustration and not stark reality, if I were a non-conforming airline I'd be concerned.) Many of the remaining 27 percent say you just can’t change your loyalty because of the airport you use.

      +

      On the Ministry of Travel thread the issue has come up, and unique bike cases like the B&W Bike Box II (below) I recently reviewed is a – pardon the pun – case in point: Is there a way to inoculate yourself against the TSA agent who doesn’t repack your bike correctly? I had a conversation with TSA’s media liaison, Lorie Dankers, and BrookHunter Whelchel, a transportation security manager at Sea-Tac Airport (Seattle).

      My specific question for Mr. Whelchel was, “Is it appropriate - is there a convention known to TSA agents - that we as travelers ask TSA for an inspection of our bikes in our presence?” The answer is yes.

      The protocol is, “Let the air carrier know when your bag is checked that you want the bag to be screened in the traveler’s presence,” according to Mr. Whelchel. “Let the air carrier know, they’ll direct you to the on-site [TSA] station; there may be someone from the airline present, assisting the TSA officer. If the passenger says ‘I’d like to be present,’ that’s perfectly fine.”

      +

      “For the actual screening process, the officer will open up the bag, do a sampling of the inside of the bag, move around items in the bag, sample the toiletry bag, the bike repair kit, if there are no alarms or security threats, the agent will repack the bag while the passenger is there.” Mr. Whelchel added that, “For security the passenger can’t repack the bike, however the passenger can guide the officer when repacking the bag, same as we do with passengers with firearms and pets.”

      I asked if this was limited to certain airports or universally available and Ms. Dankers said, “Because every airport has a unique config, sometimes you’ll encounter something a little different, but what is across the board is the access to make that request. TSA will try to work with that traveler within the limits and the airport and its footprint.”

      These TSA folks asked that I relay to you all a particular limitation to this. Every piece of baggage on a plane that originates outside the U.S. that connects to a flight inside the U.S. will be subject to search, and that won’t be done in the presence of the traveler. This includes flights from U.S. carriers. For example, last month I flew on an American Airlines flight from Madrid to Ontario, California, changing planes at Dallas DFW. My bike case was, presumably, inspected at DFW. I asked if TSA has pre-screening in any foreign airports, such as is the case with U.S. immigration, and the answer is no. I asked if there are any countries that have screening relationships with the U.S., where if a plane originates in Ireland or Israel that by virtue of their comprehensive, qualifying screening that baggage is not TSA screened at American hubs. Nope.

      I asked about guidance on the lithium batteries that are used in bikes with electronic shifting – both the batteries installed on the bikes and spare batteries we might take in either checked or carry on baggage. And, while there is watt hours guidance already listed, I was surprised to find out that TSA has nothing to do with batteries. “It’s a safety issue, not a security issue,” said Mr. Whelchel, “so the FAA will need to provide that guidance.” Ms. Dankers kindly offered to help me find my correct liaison at the FAA for this, and when I know you’ll know. I'll ask about CO2 cartridges as well.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Travel/index.html b/site/slowtwitch.com/www/Travel/index.html new file mode 100644 index 0000000..f31df88 --- /dev/null +++ b/site/slowtwitch.com/www/Travel/index.html @@ -0,0 +1,630 @@ + + + +<:: Welcome to Slowtwitch.com ::> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      + +
      + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Wrenchwork/How_to_Redecal_a_Bike_Frame_7709.html b/site/slowtwitch.com/www/Wrenchwork/How_to_Redecal_a_Bike_Frame_7709.html new file mode 100644 index 0000000..4a57307 --- /dev/null +++ b/site/slowtwitch.com/www/Wrenchwork/How_to_Redecal_a_Bike_Frame_7709.html @@ -0,0 +1,646 @@ + + + + +How to Redecal a Bike Frame - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      How to Redecal a Bike Frame

      + +
      +
      +
      +

      I’m using my OCOEE, err, my OBED as a prop for how to redecal a bike frame. The assumption is that it’s a top mount vinyl decal. When I was a bike maker we used to place very thin water-slide decals on the freshly painted bike and then clear coat over it. No changing those decals

      More recently bike companies have moved to “top mount” decals like on this bike here.

      +

      American Bicycle Group – makers of Litespeed, Quintana Roo and (now called) OBED – use a top mount decal. For reasons that attach to the (in my opinion welcome) cultural moment in which we find ourselves, ABG chose to rename its carbon gravel and offroad brand. (Here's the story behind the name OBED). I started hanging parts onto an OCOEE frame and by the time I get the brakes bled it’ll be an OBED.

      Because you may think this is a dicey operation I made a 3-minute video showing you what changing a decal looks like.

      +


      +

      Highest above ABG has a decal placement guide. You don’t really need it, because you just put the new decals where the old decals were. Nevertheless, they give it to you and here’s the page with instructions, and the downloadable decal guide. Because ABG paints its own frames in Chattanooga, and makes its own decals in-house, it could presumably send you a new set of decals for any of your ABG bikes. I find this is especially handy for titanium bikes, like ABG's Litespeed brand, because the decals on my ti bikes take a special beating for some reason.

      If you mess up, well, bummer. It’s just a decal. There’s more where these came from. But if you take any sort of care you should have no trouble. The whole thing’s a 30 minute job, whether this brand or any other. Call your bike brand’s customer service and order up a new set of decals.

      The tools for this were a hair dryer (I used a heat gun, such as what you'd use for moldable cycling shoes). That's it. ABG recommends a credit card for smoothing out bubbles, but i found that unnecessary.

      If you're an OCOEE owner, and you wish to become an OBED owner, ABG will send you a new decal set for your bike. If they don't contact you first, you'll use info@obedbikes.com.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Wrenchwork/Internal_Cable_Management_7629.html b/site/slowtwitch.com/www/Wrenchwork/Internal_Cable_Management_7629.html new file mode 100644 index 0000000..041b19d --- /dev/null +++ b/site/slowtwitch.com/www/Wrenchwork/Internal_Cable_Management_7629.html @@ -0,0 +1,653 @@ + + + + +Internal Cable Management - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Internal Cable Management

      + +
      +
      +
      +

      Is bike ownership simply an unfortunate necessity to you? Or a hobby? Because if it’s a hobby, two tasks will drive you crazy if you can’t wrestle them down: Hydraulic brake bleeding; and internal cable management. I’m writing today about the latter.

      The major themes here are tools, technique, and workflow. By workflow I mean the order of the tasks in a bike build – what parts you put on the frame first, then second, and so on. Let’s talk about this now because once we get a little further below I’m going to pivot to tools, including a product from Park Tool: the Internal Cable Routing Kit: IR-1.2 (in the image below, shown out of its plastic case and on my shop floor). Back to workflow…

      +

      When I was young whippersnapper, we began a bike build by applying our cutting tools to the frame: face the head tube, ream the inside of the head tube for press fit headset cups; face and tap the bottom bracket shell; tap the derailleur hanger threads; align the dropouts, fork ends and the derailleur hanger; and then we commence building up the frame. (But at least the cables ran external to the frame, which made up for all that!)

      After the fork is installed, then I would install the bottom bracket and the crankset. That was then. Not now! How many times have I had to uninstall the crank and the BB, because my rote brain stem behavior overrode the imperatives of today’s bikes? (And the violent abuse of a pressfit BB extraction is a part of the sausage factory you don’t want to see.)

      +

      The BB and the crankset is now the very last thing I install or, at least, it’s not installed until after all the internal cable routing is done. You very often can’t route mechanical shift cables (to either derailleur), or electronic wires to the derailleurs, or wires to the battery, or hydraulic rear brake line, with the BB installed.

      If you look at a frameset like this – the Quintana Roo PRSix2 that I built up over the weekend – this company is doing you favors. You see the vinyl liners for rear brake and for both derailleurs? I put SRAM AXS on this bike, so, I didn’t need the shift cable liners. But I left them in there anyway, because too often I’ve taken a liner out only to regret it later.

      +

      I had to take out the liner for the rear disc brake (that opaque white liner you see above), because this liner is for a mechanical disc brake cable and I’m using a hydraulic system. You have to get that hydraulic line from the back through the frame and exiting up at the front. You have to go in this direction because the hydraulic hose comes pre-attached to the brake caliper and not the lever. But that vinyl liner still becomes a useful part of the hydraulic routing process.

      Taking SRAM as an example, there’s a dome-shaped fitting on the hydraulic hose line, it’s threaded on the Stealthamajig (SRAM’s nifty hydraulic brake needle/olive hardware) and that fitting is only for cable routing. I threaded a shift cable through the lining from the front of the bike to the back, and then I passed the shift cable through a hole on this fitting. The PRSix2 had openings just big enough for the hydraulic line and the shift cable running adjacent to it. I pulled the shift cable, out came the vinyl liner, and in came the hydraulic line right behind it. You can see that technique employed in the image below, just in this image I'm routing the hydraulic line up through the fork and stem from the front brake caliper.

      +

      The process of building a bike from the frame up, the workflow, begins (!) with the routing of everything that must go through the bike’s structure: the frame, the stem, the pursuit bar and the aerobars. Let’s talk about the tools I use for this.

      They are as follows: Of prime mention is that Park Tool IR-1.2 Internal Cable Routing Kit, that comes in a little plastic box and has 4 strands of 250mm poly coated metal line, like a really thin shift cable. There is a big fat and quite strong magnet that comes with this kit. Each of the strands has a different fitting, including one that accepts a Di2 wire. On the other end are small magnets.

      The included magnet is so strong its attraction is felt by metal objects on the inside of the carbon. I often use the magnet to pull, from the outside, a cable along through the inside of a frame, and line it up with the exit hole. (This is the technique used in the image second from the top.) It can be a real bear when that exit is offline from the natural path a trace line takes, such as hydraulic lines through fork blades: Somehow that pilot cable I pass down from the top of the bike is supposed to take a left turn and exit the fork blade? By itself? A strong magnet run along the outside of the fork blade can guide the shift cable along the inside of the fork blade, and right to that exit hole.

      +

      Note that one of these strands in this Park took kit has no fixture on the end. One can use this instead of the trusty shift cable that I’ve used for so many years and, in this case, you don’t need to keep using expensive shift cables for this job.

      My only beef with this kit is that one of my favorite fittings is a boot, inside of which I can place something to be pulled along (as in the image above). But the rubber is very fragile in that boot and lasted not very long, as in 1 day (!) before it broke. Below is that boot after seeing very little action. I asked Park about this. They replied that it’s not covered under warranty. I can buy a new single replacement strand for $21.22 and, I assume, shipping. (The entire kit is a $65 item.) What they wrote me is that, “We cannot control the inside finish of a frame.” Yes. But the frame companies aren’t making their frames to perform for your boot; your boot must perform in their frames.

      Mind, I did not ask Park Tool for a replacement strand. I didn’t make a warranty claim. What I asked Park Tool is whether I could buy a single strand (which they answered) and whether they, “had this problem reported before? Do you think there's a stronger compound that might be better?" Which they didn’t answer. So let me answer. The entire kit is great, and this part is great, just, when Park Tool introduces IR-1.3 the boot should be made of a more robust rubber compound.

      +

      My other “tools” for internal routing (in the image below) are a set of picks, and you can see a couple of sets (I have way more picks than you need, and I didn’t show my total embarrassing oversupply). Then there’s an old-style elbow spoke. I use this as a pick. Truly, I use spokes like this for a lot of things. Just like toe straps, they were meant to serve a function and though that nominal function no longer exists I seem to have a lot of reasons to have these around.

      What is not shown in this image is a shift cable. I use these a lot for trace or pilot lines. I have to train myself to stop fouling my shift cables and instead use the line provided in the Park Took kit. You’ll also see a magnet in the image, but this magnet, as opposed to the Park magnet, is meant for sticking into places, attracting a shift cable or whatever it is I’m hunting for, and pulling it back out.

      +

      Also here is plastic tape, which I often use to tie a Park line, or a shift cable, to whatever pilot line it is to whatever it is I’m dragging through the frame.

      The last thing I’d say about this Park Took Kit, , is that I’ll be danged if I’m going to roll up all those lines and put them all nicely back in the tiny plastic case. Life’s too short and those strands are too long. So, this is where mine stay, and the lines drape over the top and down the back of this toolcase. The magnets are stuck onto this chest in the bottom left of that image below.

      +

      Frame companies help or hurt themselves by how hard they make this process. If you’re going to use vinyl lining, like QR does in this bike I’ve been working on, I’d like to know over time how well that lining weathers. In some bikes I’ve seen that lining get brittle and break inside the bike. I’d like to know that the lining used isn’t going to suffer that fate because if it does, how are you going to get new shift and/or brake cable through the frame? This vinyl lining is a performance product.

      In the case of this QR bike, the routing for the hydraulic lines look as if they were made for a pursuit bar that had a routing hole like this Zipp bar has. Both hydraulic brake lines came up through or just behind the stem, and the stem on this bike was made for that rear brake line to feed right into the back of the stem, without getting pinched or pulled when the handlebars turn.

      +

      Both these lines routed right into that pursuit bar's port, and if you look at the custom stem on this QR superbike (above), the hole in the stem perfectly matched up to the port you see in this handlebar.

      Now, if the brakeset we're hanging on this bike wasn’t a hydraulic system, but used a cabled system, then both the frame and the aerobars have exit ports that allow the brake housing to leave the stem, briefly see the light of day, and enter the side of the stem (image below), thereby making less of a sharp turn as they move from pursuit bar to frame.

      +

      Whether brake or shift cable, I put light grease on the cable. We need every bit of help we can get on these long runs, especially shift cables, as they go from the ends of the aerobars back to the derailleurs. The rear shift cable is typically about 220mm or so long for a tri bike, that long cable is what’s needed to run that distance, and they won’t shift well unless they pass very freely inside the housing or liner.

      I’m a big believer in electronic shifting for triathlon, and this is one of the reasons: these long cable runs, coupled with internal routing. Obviously there’s a price barrier, and this is why I’ve been nagging bike makers for a stripped down model, bare bones, entry level, with the exception of bar-end electronic shifting. Just that one upgrade.

      Until that time comes, you’ll need to deal with internal routing (and you'll need to deal with it anyway, unless brakesets someday become electronic and wireless). You must “deal” with this by handling it yourselves, using whatever counsel above might help you; or you can take this to your LBS of course and let them deal with it; or, you can let your mechanic come to you, such as those comprising the national network of Velofix mobile mechanics.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Wrenchwork/Yes_You_Can_Change_Your_Smart_Trainer_to_12-speed_7646.html b/site/slowtwitch.com/www/Wrenchwork/Yes_You_Can_Change_Your_Smart_Trainer_to_12-speed_7646.html new file mode 100644 index 0000000..410d443 --- /dev/null +++ b/site/slowtwitch.com/www/Wrenchwork/Yes_You_Can_Change_Your_Smart_Trainer_to_12-speed_7646.html @@ -0,0 +1,646 @@ + + + + +Yes, You Can Change Your Smart Trainer to 12-speed - Slowtwitch.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      +

      Yes, You Can Change Your Smart Trainer to 12-speed

      + +
      +
      +
      +

      We’ve been asked this question on our Reader Forum. Can you make your smart trainer 12-speed compatible? Yes. It’s easy, and it’s not that expensive. It’ll cost between $79 and $89 to get what’s called an XDR driver body.

      What’s the use case here? I’ve decided to put my gravel bike on my Saris H3 trainer. That bike has a SRAM AXS 12sp system with 10-tooth 1st position cog. The H3 trainer has what your trainer almost certainly has, if you’ve got a smart trainer: a driver body that’s made for an 11sp system, with a 1st position cog no smaller than 11 teeth. What you need to do to adapt your trainer to a SRAM 12sp system replace the driver body.

      +

      You can buy new wheels today from just about every wheel maker with an XDR driver body on the hub. You spec this when you buy the wheel. You can also change out the driver body of your existing 11sp wheelset to match the new SRAM AXS drivetrain on the bike you just bought. Probably. We’re working now on a comprehensive chart showing which wheels from every company are upgradeable to an XDR driver body.

      But we’re limiting this, today, to changing out your driver body on your smart trainer. Most Saris, Tacx, Wahoo smart trainers can take an XDR driver body, and all these companies sell their version of the driver body. Just, check with your smart trainer company if that trainer is older than, say, 2 years old, to make sure it’s upgradeable.

      Above is a pic of what you get when you get sent a new driver body. Below is my badly lit, badly produced video showing me changing out the driver body on my Saris H3.

      +

      +

      Fortunately, my video isn’t very long! I hope you can see how easy this is. As for tools, basically, can you change a cassette? Do you have the tools for that? A chain whip or chain pliers. (Chain pliers better, but chain whip used in this vid.) You need a cassette spline tool. That’s it, in terms of bike-specific tools. Okay, a cone wrench. But that’s not really a requirement. You should be able to muddle through with a set of open/box-end wrenches or a crescent wrench (adjustable spanner for our UK readers).

      That’s it! Simple. Happy riding.

      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      + +
      + +
      +
      + +
      + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/Wrenchwork/index.html b/site/slowtwitch.com/www/Wrenchwork/index.html new file mode 100644 index 0000000..3a80199 --- /dev/null +++ b/site/slowtwitch.com/www/Wrenchwork/index.html @@ -0,0 +1,630 @@ + + + +<:: Welcome to Slowtwitch.com ::> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      +
      +
      +
      +Swim +Bike +Run +
      +
      + + +
      + + +
      +
      + +
      +
      +
      + +
      +
      +
      + +
      + +
      +
      +
      + + +
      +
      + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + +
      +
      +
      +
      + +
      + + + + + + + + + + diff --git a/site/slowtwitch.com/www/adio/styles.css b/site/slowtwitch.com/www/adio/styles.css new file mode 100644 index 0000000..d2bd5d1 --- /dev/null +++ b/site/slowtwitch.com/www/adio/styles.css @@ -0,0 +1,545 @@ +/* Defaults */ +body { +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +color: #000; +padding: 0; +margin: 15px 0 5px; +} + +.indent { +margin-left: 10px; +} + +.indentlarge { +margin-left: 30px; +} + +.indenthuge { +margin-left: 50px; +} + +.rightside { +text-align: right; +} + +form { +margin: 0; +padding: 0; +} + +/* Links +a:link { +color: #000099; +text-decoration : none; +padding : 0; +margin : 0; +} +a:visited { +color: #000099; +text-decoration : none; +} +a:hover { +color: #000000; +text-decoration : underline; +} +a:active { +color: #999999; +text-decoration : none; +}*/ + +.modify{ +text-decoration: none; +color: #FFFFFF; +font-weight: bold; +font-size: 14pt; +} + +.modify:hover{ +text-decoration: underline; +} + +/* Outer layer */ + +#outer { +text-align: left; +background: #ffffff; +color: inherit; +/**border: 1px solid #000000;**/ +margin: 0 auto; +padding: 0; +} + +/* Header */ + +.header { +background: #fcce32; +} + +.logolink { +padding: 6px 3px; +} + + .logolink a { +background: inherit; +} + +.hdr-links { +width: 230px; +text-align: center; +font-size : 10px; +padding : 0; +margin: 0; +} + +a.hdrnav:link { +color: #000099; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +} +a.hdrnav:visited { +color: #000099; +background : inherit; +text-decoration : none; +} +a.hdrnav:hover { +color: #000; +background : #fff; +text-decoration : underline; +} +a.hdrnav:active { +color: #999; +background : inherit; +text-decoration : none; +} + +.hdr-links .access { +text-decoration : underline; +color : #000; +background : inherit; +} + + +/* Top Nav Bar */ + +.top-nav { +height: 25px; +background: #c5c6ac; +color: inherit; +border: solid #000000; +border-width: 1px 0; +} +.top-nav-inner { +padding: 5px 10px; +} +.top-nav-inner a:link { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: none; +} +.top-nav-inner a:visited { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: none; +} +.top-nav-inner a:hover { +background: inherit; +color: #555555; +font-weight: bold; +text-decoration: underline; +} +.top-nav-inner a:active { +color: #000000; +background: inherit; +font-weight: bold; +text-decoration: underline; +} + + + +/* Image Bar */ + +.image-bar { +clear:both; +} + +.title-img { +border: 1px solid #000; +color: #ffffff; +background: #343234; +padding: 0 0 0 10px; +} + +.section-title { +font-size: 16px; +font-weight: bold; +font-family: verdana, arial, tahoma, sans-serif; +} + +.graphic-img { +border: 1px solid #000000; +} + +.blank-img { +border: 1px solid #000000; +background: #fcce32; +} + +.blank-img-ads { +border: 1px solid #000000; +background: #fcce32; +} + + +/* Content Section */ + +/*.content { +padding: 0 4px 4px 4px; +margin: 0; +}*/ + +.left-col { +border: 1px solid #000000; +background: #fcce32; +width: 145px; +} + +.left-nav { +margin: 3px; +} + +.statenav { + margin: 4px; +} + +.lnav-hdr { +background: #c5c6ac; +color: #000000; +font-family: verdana, tahoma, arial, sans-serif; +font-size: 11px; +font-weight: bold; +border: 1px solid #000000; +margin-top: 16px; +padding: 2px 5px; +} + +.left-nav p { +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +line-height: 14px; +margin:5px; +} + +.left-nav .access { +text-decoration : underline; +color : #000000; +background : inherit; +} + +a.lhdr:link { +color: #000000; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +font-weight: bold; +} +a.lhdr:visited { +color: #000000; +background : inherit; +text-decoration : none; +font-size: 10px; +font-weight: bold; +} +a.lhdr:hover { +color: #000; +background : #fcfefc; +text-decoration : underline; +} +a.lhdr:active { +color: #555; +background : inherit; +text-decoration : none; +} + +input.nobutton { + background-color: #0A50A1; + font-weight: bold; + font-size: 9px; + color: white; +} + +input.nobutton:visited { + background-color: #fcce32; + font-weight: bold; + font-size: 9px; + color: white; +} + +input.nobutton:active { + background-color: #fcce32 + color: white; + font-weight: bold; + font-size: 9px; +} + +a.nav:link { +color: #000099; +background: inherit; +text-decoration : none; +font-size : 10px; +padding : 0; +margin : 0; +} +a.nav:visited { +color: #000099; +background : inherit; +text-decoration : none; +} +a.nav:hover { +color: #000; +background : #fcfefc; +text-decoration : underline; +} +a.nav:active { +color: #999; +background : inherit; +text-decoration : none; +} + + + +/* Main content box */ +.content-box { +padding: 0; +margin: 0; +} + +/* right content box */ +.rhcol { +float: right; +width: 160px; +margin: 0 0 10px 15px; +background: #fcfefc; +color: #000000; +} +.rhcont { +border-left: 1px solid #000000; +border-bottom:3px solid #000000; +} +.rnav-hdr { +background: #c5c6ac; +color: #000000; +font-family: verdana, tahoma, arial, sans-serif; +font-size: 12px; +font-weight:bold; +border-bottom: 1px solid #000; +border-left: 2px solid #000000; +margin: 3px 0; +padding: 2px; +} + +/* main content area */ + +.content-title { +margin: 5px 0px 15px; +font-size: 14px; +text-decoration: underline; +} + +.bottomindent { +margin-bottom: 5px; +} + +.content-title-blog { +margin: 5px 0px 2px; +margin-left: 8px; +font-size: 16px; +text-decoration: none; +} + +.content-title-no1 { +margin: 5px 0px 15px; +font-size: 14px; +text-decoration: none; +} + +.content-title-mod { +margin: 5px 10px 15px; +font-size: 14px; +text-decoration: underline; +} + +.content-title-no { +margin: 0px 0px 0px; +font-size: 12px; +text-decoration: none; +} + +.content-box .cont { +margin: 10px 10px 15px 10px; +font-size: 11px; +line-height: 15px; +} + +.content-box .cont-list { +margin: 2px 10px 5px 10px; +} + +.content-box .sub-hdr { +margin: 2px 10px 10px; +font-size: 12px; +text-decoration: underline; +} + +.content-box .sub-hdr-mod { +margin: 2px 10px 10px; +font-size: 11px; +text-decoration: none; +} + +/* Bottom footer box */ + +.footer-col { +margin: 0 0 0 4px; +} + +.footer-box { +border: 1px solid #000000; +background:#343234; +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +color:#ffffff; +padding: 0 0 0 15px; +} + +.footer-box .cont1 { +margin: 8px 10px 5px 5px; +font-size: 11px; +line-height: 15px; +} +.footer-box .cont2 { +margin: 2px 10px 15px 5px; +font-size: 11px; +line-height: 15px; +} + +.footer-box input { +font-family: verdana, arial, tahoma, sans-serif; +font-size: 11px; +} + +/* Copyright layer */ + +.copyright { +background: #c5c6ac; +font-family: verdana, arial, tahoma, sans-serif; +font-size: 10px; +font-weight: bold; +padding: 6px; +border: solid #000000; +border-width: 1px 0 0; +} + +.rh-ad { +padding: 10px 0; +} + +/* Terms of use on front page */ +.tou { +margin: 0 0 0 10px; +} + + +/* for templates info */ +.temp-lh { +float : left; +width : 100px; +margin : 0; +padding : 2px 10px 2px 0; +text-align : left; +} +.temp-rh { +margin : 0; +padding : 2px 0; +text-align : left; +} +.date { +font-size : 10px; +color : #787d63; +background : inherit; +} +.temp-list ul { +font-size : 11px; +margin : 0; +padding : 0; +list-style-type : none; +} +.temp-list { +padding : 0 0 0 15px; +} + +/* small*/ +small.fsize { +font-size : 9px; +color : #999999; +background : inherit; +} + +.code { +color : #663300; +background : #eeeeee; +font-family : verdana, arial, sans-serif; +font-size : 11px; +line-height : 20px; +padding : 4px 10px; +margin : 10px 20px; +border : 1px dashed #999999; +} + + +/* Search form on front page */ + +.search-form { +color: #000; +background: #c5c6ac; +padding: 5px; +margin: 0px 0 10px 30px; +border: 1px solid #333333; +width:465px; +text-align: center; +white-space: nowrap; +} + +/* Search forms on Search page */ + +.adv-search { +margin: 0 0 0 15px; +} + +/* Tool Tip */ +.tip { +cursor : help; +border-bottom : 1px dashed #555555; +} + +/* ADVERTISEMENTS */ +/* container div is 'id=adspace' */ +#adspace { +background: inherit; +} +.ads { +background: inherit; +margin: 5px 0 15px 4px; +font-size: 11px; +padding: 5px; +border: solid #555; +border-width: 1px 0 0 1px; +} +.ads a:link { font-size: 11px; } diff --git a/site/slowtwitch.com/www/articles/static/admin/treecats.css b/site/slowtwitch.com/www/articles/static/admin/treecats.css new file mode 100644 index 0000000..b926f7a --- /dev/null +++ b/site/slowtwitch.com/www/articles/static/admin/treecats.css @@ -0,0 +1,60 @@ +/* + * ================================================================= + * Gossamer Links - enhanced directory management system + * + * Website : http://gossamer-threads.com/ + * Support : http://gossamer-threads.com/scripts/support/ + * Revision : $Id: treecats.css,v 1.3 2006/08/11 04:30:58 brewt Exp $ + * + * Copyright (c) 2005 Gossamer Threads Inc. All Rights Reserved. + * Redistribution in part or in whole strictly prohibited. Please + * see LICENSE file for full details. + * ================================================================= + */ + +.treecats-selection-summary ul, .treecats-selection ul { + margin: 0px; + padding: 0px; +} +.treecats-selection-summary li, .treecats-selection li { + list-style: none; +} +.treecats-selection-summary li a, .treecats-selection li a { + margin-top: 2px; + margin-left: 5px; +} +.treecats-selection-summary a, .treecats-selection a { + font-size: 9px; +} +.treecats-selection-summary a:visited, .treecats-selection a:visited { + color: #212126; +} +.treecats-selection-current { + font-weight: bold; +} +.treecats-category-info img { + border: 0px; + margin: 2px; + vertical-align: middle; +} +.treecats-children { + padding-left: 15px; +} +.treecats-selected span, li.treecats-selected { + font-weight: bold; +} +.treecats-category a:link, .treecats-category a:visited { + color: #212126; + text-decoration: none; +} +.treecats-category a:hover { + text-decoration: underline; +} +ul.treecats-links { + padding-left: 15px; + margin: 0px; +} +.treecats-links li { + line-height: 1.25em; + list-style: circle; +} diff --git a/site/slowtwitch.com/www/articles/static/css b/site/slowtwitch.com/www/articles/static/css new file mode 120000 index 0000000..aa508ad --- /dev/null +++ b/site/slowtwitch.com/www/articles/static/css @@ -0,0 +1 @@ +/var/home/slowtwitch/site/common/static/css \ No newline at end of file diff --git a/site/slowtwitch.com/www/articles/static/fileman/fileman.css b/site/slowtwitch.com/www/articles/static/fileman/fileman.css new file mode 100644 index 0000000..65bf9dd --- /dev/null +++ b/site/slowtwitch.com/www/articles/static/fileman/fileman.css @@ -0,0 +1,12 @@ +a:active { font-weight: normal; color: #0000FF; text-decoration: none} +a:hover { color: #CC0000; text-decoration: none} +.header_format { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 9pt; font-weight: bold; color: #FFFFFF; background-color: #326AAF} +.text_format { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 9pt ; color: #OOOOOO} +.object { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 9pt; color:#000000; background-color: #FFFFFF ; border:1px solid #A1A576;} +.submit { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 8pt; color: #000066; font-weight: bold; border:1px solid #A1A576} +.highlight { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 9pt; color:#CC0000; background-color:#E6E6FF} +.background { background-color: #E9E9E9; color:#000066 } +.bg_frames { background-color: #D3D3D3; color:#000066 } +.bg_main { background-color: #FFFFFF; color:#000066} +.bg_main2 { background-color: #D8D8D8; } +.bg_window { background-color: #E9E9E9; } diff --git a/site/slowtwitch.com/www/articles/static/fileman/gt.css b/site/slowtwitch.com/www/articles/static/fileman/gt.css new file mode 100644 index 0000000..4ac0bce --- /dev/null +++ b/site/slowtwitch.com/www/articles/static/fileman/gt.css @@ -0,0 +1,12 @@ +a:active { font-weight: normal; color: #0000FF; text-decoration: none} +a:hover { color: #CC0000; text-decoration: none} +.header_format { background-color: #256A19; font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 9pt; font-weight: bold; color: #FFFFFF} +.text_format { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 9pt ; color: #OOOOOO} +.object { background-color:#D2EDB8; color: black; font-family: Arial, Verdana, Helvetica, sans-serif; font-size: 12px; } +.submit { background-color:#d9e4f2; font-family: Arial,Verdana,Helvetica, sans-serif; font-size:12px; color:black} +.highlight { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 9pt; color:#0033CC; background-color: #FFCECE} +.background { background-color: #E6F5D7; color:#000000 } +.bg_frames { background-color: #E6F5D7; color:#000000 } +.bg_main { background-color: #FFFFFF; } +.bg_main2 { background-color: #CDD6C0; } +.bg_window { background-color: #d9e4f2; border: #d9e4f2} diff --git a/site/slowtwitch.com/www/articles/static/fileman/luna/cluetip.css b/site/slowtwitch.com/www/articles/static/fileman/luna/cluetip.css new file mode 100644 index 0000000..e73f0e6 --- /dev/null +++ b/site/slowtwitch.com/www/articles/static/fileman/luna/cluetip.css @@ -0,0 +1,176 @@ +/* global */ +#cluetip-close img { + border: 0; +} +#cluetip-title { + overflow: hidden; +} +#cluetip-title #cluetip-close { + float: right; + position: relative; +} +#cluetip-waitimage { + width: 43px; + height: 11px; + position: absolute; +} +.cluetip-arrows { + display: none; + position: absolute; + top: 0; + left: -11px; + height: 22px; + width: 11px; + background-repeat: no-repeat; + background-position: 0 0; +} +#cluetip-extra { + display: none; +} +/*************************************** + =cluetipClass: 'default' +-------------------------------------- */ + +.cluetip-default { + margin: 0px; + padding: 0px; + background: #edf2cb; + border: 3px solid #3e3d45; +} +.cluetip-default #cluetip-outer { + position: relative; + margin: 0; + padding: 0; + background: #edf2cb; +} +.cluetip-default h3#cluetip-title { + margin: 0 0 0px; + font-size: 11px; + padding: 3px 5px 2px 5px; + background: #5d5b66; + color: #fff; + font-weight: bold; +} +.cluetip-default #cluetip-title a { + color: #d9d9c2; +} +.cluetip-default #cluetip-inner { + padding: 10px; +} +.cluetip-default div#cluetip-close { + text-align: right; + margin: 0 5px 5px; + color: #900; +} + +/*************************************** + =cluetipClass: 'jtip' +-------------------------------------- */ +.cluetip-jtip { + background-color: transparent; +} +.cluetip-jtip #cluetip-outer { + border: 2px solid #ccc; + position: relative; + background-color: #fff; +} + +.cluetip-jtip h3#cluetip-title { + margin: 0 0 5px; + padding: 2px 5px; + font-size: 16px; + font-weight: normal; + background-color: #ccc; + color: #333; +} +.cluetip-jtip #cluetip-inner { + padding: 0 5px 5px; + display: inline-block; +} +.cluetip-jtip div#cluetip-close { + text-align: right; + margin: 0 5px 5px; + color: #900; +} + +/*************************************** + =cluetipClass: 'rounded' +-------------------------------------- */ + +.cluetip-rounded { + background: transparent url(images/bl.gif) no-repeat 0 100%; + margin-top: 10px; + margin-left: 12px; +} + +.cluetip-rounded #cluetip-outer { + background: transparent url(images/tl.gif) no-repeat 0 0; + margin-top: -12px; +} + +.cluetip-rounded #cluetip-title { + background-color: transparent; + padding: 12px 12px 0; + margin: 0 -12px 0 0; + position: relative; +} +.cluetip-rounded #cluetip-extra { + position: absolute; + display: block; + background: transparent url(images/tr.gif) no-repeat 100% 0; + top: 0; + right: 0; + width: 12px; + height: 30px; + margin: -12px -12px 0 0; +} +.cluetip-rounded #cluetip-inner { + padding: 5px 12px 12px; + margin: -18px -12px 0 0; + position: relative; +} + +.cluetip-rounded div#cluetip-close { + text-align: right; + margin: 0 5px 5px; + color: #009; + background: transparent; +} +.cluetip-rounded div#cluetip-close a { + color: #777; +} + +/* rounded arrows */ + +.clue-right-rounded .cluetip-arrows { + background-image: url(images/rarrowleft.gif); +} +.clue-left-rounded .cluetip-arrows { + background-image: url(images/rarrowright.gif); + left: 100%; + margin-left: 12px; +} +.clue-top-rounded .cluetip-arrows { + background-image: url(images/rarrowdown.gif); + top: 100%; + left: 50%; + margin-left: -11px; + height: 11px; + width: 22px; +} +.clue-bottom-rounded .cluetip-arrows { + background-image: url(images/rarrowup.gif); + top: -23px; + left: 50%; + margin-left: -11px; + height: 11px; + width: 22px; +} + + + +/* stupid IE6 HasLayout hack */ +.cluetip-rounded #cluetip-title, +.cluetip-rounded #cluetip-inner { + zoom: 1; +} diff --git a/site/slowtwitch.com/www/articles/static/fileman/luna/datePicker.css b/site/slowtwitch.com/www/articles/static/fileman/luna/datePicker.css new file mode 100644 index 0000000..1e39635 --- /dev/null +++ b/site/slowtwitch.com/www/articles/static/fileman/luna/datePicker.css @@ -0,0 +1,115 @@ +table.jCalendar { + border: 1px solid #000; + background: #aaa; + border-collapse: separate; + border-spacing: 2px; +} +table.jCalendar th { + background: #333; + color: #fff; + font-weight: bold; + padding: 3px 5px; +} +table.jCalendar td { + background: #ccc; + color: #000; + padding: 3px 5px; + text-align: center; +} +table.jCalendar td.other-month { + background: #ddd; + color: #aaa; +} +table.jCalendar td.today { + background: #666; + color: #fff; +} +table.jCalendar td.selected { + background: #f66; + color: #fff; +} +table.jCalendar td.selected:hover { + background: #f33; + color: #fff; +} +table.jCalendar td:hover, table.jCalendar td.dp-hover { + background: #fff; + color: #000; +} +table.jCalendar td.disabled, table.jCalendar td.disabled:hover { + background: #bbb; + color: #888; +} + +/* For the popup */ + +/* NOTE - you will probably want to style a.dp-choose-date - see how I did it in demo.css */ + +div.dp-popup { + position: relative; + background: #ccc; + font-size: 10px; + font-family: arial, sans-serif; + padding: 2px; + width: 171px; + line-height: 1.2em; +} +div#dp-popup { + position: absolute; + z-index: 5000; +} +div.dp-popup h2 { + font-size: 12px; + text-align: center; + margin: 2px 0; + padding: 0; +} +a#dp-close { + font-size: 11px; + padding: 4px 0; + text-align: center; + display: block; +} +a#dp-close:hover { + text-decoration: underline; +} +div.dp-popup a { + color: #000; + text-decoration: none; + padding: 3px 2px 0; +} +div.dp-popup div.dp-nav-prev { + position: absolute; + top: 2px; + left: 4px; + width: 100px; +} +div.dp-popup div.dp-nav-prev a { + float: left; +} +/* Opera needs the rules to be this specific otherwise it doesn't change the cursor back to pointer after you have disabled and re-enabled a link */ +div.dp-popup div.dp-nav-prev a, div.dp-popup div.dp-nav-next a { + cursor: pointer; +} +div.dp-popup div.dp-nav-prev a.disabled, div.dp-popup div.dp-nav-next a.disabled { + cursor: default; +} +div.dp-popup div.dp-nav-next { + position: absolute; + top: 2px; + right: 4px; + width: 100px; +} +div.dp-popup div.dp-nav-next a { + float: right; +} +div.dp-popup a.disabled { + cursor: default; + color: #aaa; +} +div.dp-popup td { + cursor: pointer; +} +div.dp-popup td.disabled { + cursor: default; +} diff --git a/site/slowtwitch.com/www/articles/static/fileman/luna/luna.css b/site/slowtwitch.com/www/articles/static/fileman/luna/luna.css new file mode 100644 index 0000000..4bbaa7b --- /dev/null +++ b/site/slowtwitch.com/www/articles/static/fileman/luna/luna.css @@ -0,0 +1,82 @@ +@import url(luna_core.css); + +/* +This file inherits from luna_core.css, so styles in this file will override +styles defined in it. Changes to this file will NOT be overwritten during +upgrades. + +Here's a quick tutorial on overriding styles. Say you want to change the +background colour of the body. Looking at luna_core.css, the colour is +defined in the rule: + + body { + margin: 0px; + padding: 0px; + color: #33332e; + background: #ffffff; + font: normal 11px tahoma, geneva, verdana, sans-serif; + } + +To change the background colour of white (#ffffff) to gray (#dddddd), you would +add the following to this file: + + body { + background: #dddddd; + } + +If you also wanted to change the font as well as the background, then you could +use: + + body { + background: #dddddd; + font: normal 12px times new roman; + } + +Also note that every template's body id is assigned the template name (without +the .htm extension). So if you want to change the h2 heading in the +home.htm template, you can do this by: + + #home h2 { + color: red; + font-weight: bold; + } + +To start you off, some sample overriding styles have been provided below. Note +that you will have to uncomment the rules for them to work. +*/ + +/* Logo size and image source */ +/* +#logo { + width: 250px; + height: 80px; + background-image: url(images/logo.gif); +} +*/ +/* If the height of your logo changes from the original, then you will probably +want to also change the vertical position of the login link */ +/* +#loginbar a { + margin-top: 25px; +} +*/ + +/* Change width of document */ +/* +#wrapper { + width: 750px; +} +*/ + +/* Don't want the shadows? */ +/* +.shadowtop, .shadowbottom, .shadowleft, .shadowright { + background: none; +} +.shadowtopleft, .shadowtopright, .shadowbottomleft, .shadowbottomright { + width: auto; + height: auto; + float: none; + background: none; +} +*/ diff --git a/site/slowtwitch.com/www/articles/static/fileman/luna/luna_core.css b/site/slowtwitch.com/www/articles/static/fileman/luna/luna_core.css new file mode 100644 index 0000000..effc111 --- /dev/null +++ b/site/slowtwitch.com/www/articles/static/fileman/luna/luna_core.css @@ -0,0 +1,1298 @@ +/* + * ================================================================= + * Gossamer Mail - enhanced email management system + * + * Website : http://gossamer-threads.com/ + * Support : http://gossamer-threads.com/scripts/support/ + * Revision : $Id: luna_core.css,v 1.37 2008/11/26 20:38:28 brewt Exp $ + * + * Copyright (c) 2005 Gossamer Threads Inc. All Rights Reserved. + * Redistribution in part or in whole strictly prohibited. Please + * see LICENSE file for full details. + * ================================================================= + */ + +/* +Do not make changes to this file. Any changes will be overwritten the next +time you upgrade Gossamer Mail. You can override any css in this file by +defining overriding styles in luna.css. +*/ + +/* +There are a few CSS filters/hacks used in this file to fix bugs in various +browsers. Here is a list of them: + +- Star html selector bug (css rule only applies to ie/win) + http://www.info.com.ph/~etan/w3pantheon/style/starhtmlbug.html + eg. * html {} +- Simplified box model hack (hide css from ie5-5.5/win) + http://www.doxdesk.com/personal/posts/css/20020212-bmh.html + eg. w\idth: 100px; +- Commented backslash (hide css from ie5/mac) + http://www.sam-i-am.com/work/sandbox/css/mac_ie5_hack.html +*/ + +/*------------------*\ +|* global interface *| +\*------------------*/ +body { + margin: 0px; + padding: 0px; + color: #33332e; + background: #ffffff; + font: normal 11px tahoma, geneva, verdana, sans-serif; + overflow: hidden; +} +img { + border: none; +} + +/* forms */ +form { + margin: 0px; +} +input, textarea, select { + font: normal 13px tahoma, geneva, verdana, sans-serif; +} +input.text, input.password, textarea { + padding: 1px 0px 1px 2px; + border: 1px solid #57594b; +} +input.checkbox { + margin: 4px 4px 4px 0px; +} +select { + border: 1px solid #57594b; +} + +/* links */ +a, a:visited { + color: #212126; +} +a:hover { + color: #5c5b66; + text-decoration: none; +} + +/* headings */ +h1.header { + font-size: 16px; +} +h2.header { + font-size: 15px; +} +h3.header { + font-size: 13px; + text-align: right; + padding: 0px 5px 10px 0px; + margin: 0px 0px 1px 0px; +} +h4.header { + font-size: 12px; +} +h5.header { + font-weight: normal; + font-size: 11px; +} +#wrapper { + margin: 0px; +} +.noauth #wrapper { + text-align: center; +} +/*-----------*\ +|* home page *| +\*-----------*/ +#home { + overflow: hidden; +} +/*--------*\ +|* header *| +\*--------*/ +#logo { + width: 205px; + height: 50px; + background: transparent url(images/logo.gif) top left no-repeat; + text-indent: -9999px; +} +#logo h1, #logo a { + margin: 0px; + width: 100%; + height: 100%; +} +#logo a { + text-decoration: none; + display: block; +} +#header { + background: transparent url(images/swirls.gif) bottom right no-repeat; + margin: 3px 0px; +} +#loginbar { + float: right; + text-align: right; + margin-right: 10px; +} +#loginbar a { + margin-top: 8px; + padding: 10px 30px 10px 0px; + color: #212126; + background: transparent url(images/logout.gif) center right no-repeat; + font-weight: bold; + display: block; +} +#loginbar a:hover { + color: #5c5b66; +} +.noauth #header { + background: transparent url(images/larger_swirls.gif) top right no-repeat; + border-bottom: 3px solid #383741; + text-align: left; +} +.noauth #logo { + width: 260px; + height: 80px; + background: transparent url(images/logo_large.gif) top left no-repeat; +} +/*------------*\ +|* navigation *| +\*------------*/ +.navbar { + position: relative; + margin-bottom: 1px; + height: 22px; + background: url(images/betweennav_hor.gif) #5c5b66 bottom left repeat-x; +} + +ul.primarynav { + position: absolute; + margin: 0px; + padding: 0px; + width: 100%; + list-style: none; +} +ul.primarynav li.navitem { + position: relative; + float: left; + list-style: none; + display: block; +} +ul.primarynav li.navitem a { + margin-right: 1px; + font-size: 11px; + padding: 4px 0; +} +ul.primarynav input.submit { + background: #5c5b66; + border: 0px; + color: #ffffff; + font-size: 11px; + margin-right: 1px; + padding: 2px 5px; + cursor: pointer; + cursor: hand; +} +ul.primarynav input.submit:hover { + background: #737180; +} +ul.primarynav input.selected { + background: #383741; +} +#primarynav li.navitem a { + color: #ffffff; + padding: 3px 8px; + display: block; + text-decoration: none; +} +#primarynav li.navitem a:visited { + color: #ffffff; +} +#primarynav li.navitem a.navlink:hover { + color: #ffffff; + background: #737180; +} +#primarynav li.navitem a.sublink:hover { + padding-bottom: 6px; +} +#primarynav li.selected a { + background: #383741; +} +#primarynav { + top: 57px; + left: 0px; +} +#primarynav li.navitem { + background: transparent url(images/betweennav.gif) bottom right no-repeat; +} +#breadcrumbsnav { + top: 84px; + left: 10px; + width: 90% +} +#breadcrumbsnav li.navitem ul { + top: 13px; +} +#breadcrumbsnav li.navitem ul li a, #breadcrumbsnav li.nav-item ul li a:visited { + padding-left: 5px; + color: #fff; + text-decoration: none; +} +#breadcrumbsnav li.navitem img { + margin: 2px 2px 0 0; +} +#breadcrumbsnav img, #breadcrumbsnav span { + vertical-align: middle; +} +a.root { + font-weight: bold; +} +/*--------------*\ +|* Toolbar menu *| +\*--------------*/ +ul.primarynav ul { + position: absolute; + top: 20px; + left: 0px; + width: 200px; + display: none; + background: #737180; + margin: 0px; + padding: 0px; + border: 1px solid #ccc; + z-index: 1000; +} +ul.primarynav iframe { + position: absolute; + width: 190px; + border: 0; + top: 20px; + left: 0px; + z-index: 500; +} + +ul.primarynav li.navitem:hover ul, ul.primarynav li.sfhover ul, ul.primarynav li.navitem:hover ul, ul.primarynav li.sfhover iframe { + display: block; + list-style: none; +} +ul.primarynav li.sfhover ul li { + padding: 0px; +} +ul.primarynav li.navitem:hover ul li hr, ul.primarynav li.sfhover ul li hr { + margin: 0px; + padding: 0px; + height: 1px; +} +ul.primarynav li.navitem:hover ul li a, ul.primarynav li.sfhover ul li a { + display: block; +} +ul.primarynav li.navitem:hover ul li a:hover, ul.primarynav li.sfhover ul li a:hover { + background: #a09fac; +} +/*-----------*\ +|* searchbar *| +\*-----------*/ +#cdbar { + position: relative; + margin-bottom: 3px; + padding: 3px 10px; + border-bottom: 1px solid #57594b; + background: #edf2cb; + height: 16px; +} +#cdbar #cdform { + position: absolute; + top: 3px; + right: 3px; + z-index: 100; +} +#cdbar input.text, #cdbar input.submit { + margin-right: 5px; + font-size: 10px; +} +#cdbar input.submit { + padding: 0px 3px; + height: 19px; + line-height: 1em; +} +/* these colours are the same as defined by 'a', but we unset a:visited */ +#cdbar { + padding: 5px 3px 5px 8px; +} +#cdbar a { + color: #212126; + font-size: 10px; +} +#cdbar a:hover { + color: #5c5b66; +} +#cdbar .username { + padding-top: 4px; + float: right; +} +/*--------*\ +|* footer *| +\*--------*/ +#footer { + margin-top: 3px; + padding: 5px; + border-top: 1px solid #57594b; + background: #edf2cb; +} +#footer img { + float: right; +} +#footer p { + margin: 0px; + padding: 10px 0px; + color: #8a8d77; + font-size: 9px; + text-align: left; +} + +/*---------------*\ +|* contentheader *| +\*---------------*/ +#wrapper-content { + position: relative; + background: #e2e1eb; + width: 100%; +} +#contentheader { + position: absolute; + padding: 7px 10px; + background: #e2e1eb; + height: 54px; + display: none; +} +#contentheader input, #contentheader select, #contentheader label { + vertical-align: middle; +} +#contentheader input, #contentheader select { + vertical-align: middle; +} +#contentheader .row { + padding: 0px; +} +#contentheader .row .value { + padding: 1px 0; +} +#contentheader .row label.name { + width: 50px; + padding: 2px 0px; +} +#contentheader .row .value select { + width: auto; + margin: 1px 0; +} +#contentheader .row .value label { + padding-right: 5px; +} +#contentheader .switch-mode { + float: right; + font-weight: bold; + display: none; +} +#contentheader .longrun { + padding-left: 25%; +} +#contentheader input.longtext { + width: 40%; +} +#cmd-upload input.file { + font-size: 11px; + border: 1px solid #828284; +} +.contentframe { + overflow-y: scroll; +} +.contentframe .preview-image { + padding: 5px 0; +} + +/*--------------------\ +|* Error and message *| +\*-------------------*/ +#contentmessage { + margin: 2px 8px 0 8px; + padding: 2px 2px 0 2px; + border: 1px solid #ffffff; + color: #ffffff; + font-size: 12px; + height: 16px; +} +.error { + background: #8c3030; +} +.message { + background: #3a5841; + vertical-align: middle; +} +.error *, .message * { + margin: 0px; + padding: 0px; +} +.noauth #contentmessage { + border: 0px; + margin: 0px auto; + padding: 0px; + width: 690px; + widt\h: 685px; +} +.noauth #contentmessage .error, .noauth #contentmessage .message { + border: 1px solid #fff; + margin: 0; +} +#reset_password .row label.name { + width: 60px; +} +/*---------------*\ +|* contentfooter *| +\*---------------*/ +#contentfooter { + background: #e2e1eb; + padding: 0px 10px 4px 10px; + font-size: 10px; +} +#contentfooter div.summary { + float: right; +} +#contentfooter span { + font-weight: bold; +} + +/*---------*\ +|* content *| +\*---------*/ +#ocwrapper { + position: absolute; + width: 100%; + left: 0; +/* background colour of left sidebar */ + border-left: 0px solid #e2e1eb; +/* background colour of right sidebar */ + border-right: 0px solid #e2e1eb; + background: #e2e1eb; +} +#contentwrapper { + width: 100%; + float: left; + position: relative; +} +#content { + margin: 0px 10px; + padding: 10px; + background: #ffffff; + text-align: left; +} +#content h1, #content h2 { + margin: 0px; + padding: 0px; +} +.noauth #content h2 { + color: #ffffff; + background: #383741; + text-transform: uppercase; + font-size: 11px; + padding: 3px 4px; + margin-top: 0px; +} +.noauth #content h3 { + font-size: 13px; + text-align: right; + padding-bottom: 5px; + margin: 10px 0px 1px 0px; + background: transparent url(images/row-separator.gif) repeat-x bottom left; +} +.noauth #content h4 { + font-size: 12px; +} +.noauth #content h5 { + font-weight: normal; + font-size: 11px; +} +.noauth #ocwrapper { + border: none; + position: relative; +} +.noauth #contentwrapper { + margin: 0px auto; + width: 700px; + float: none; +} +.noauth .lostpasswd { + float: right; + padding-top: 5px; +} + +/* hack for ie/win's guillotine bug */ +/* start non ie/mac css \*/ +* html #content { + height: 1%; +} +/* end non ie/mac css */ + +/*------------------------------*\ +|* actions and paging *| +\*------------------------------*/ +.toolbar { + padding: 5px; + border-top: 1px solid #ffffff; + border-bottom: 1px solid #ffffff; + background: #cac9d1; +} +.toolbar input.text { + width: 300px; +} +.toolbar input.short-text { + width: 100px; +} +.toolbar input, .toolbar select { + vertical-align: middle; +} +.toolbar select.action { + float: left; + margin-right: 15px; +} +.toolbar .action-form { + float: left; +} +.toolbar input.submit, .modal-dialog input.submit { + padding: 0px 5px; + font-size: 11px; + font-weight: bold; +} +#group div { + float: left; +} +#group span, #group label, #group input { + vertical-align: middle; +} +#group .label { + margin: 0 3px 0 10px; + line-height: 10px; + text-align: center; +} +#group .label span { + font-size: 9px; +} +#group input.text { + width: 35px; +} +.toolbar .group { + border: 1px solid #85817a; + padding: 0px 3px 3px 3px; + margin: 0px 1px 0 0; +} +.toolbar .group span { + background: #cac9d1; + font-size: 9px; + font-weight: bold; +} +.toolbar .group input.checkbox { + margin: 0px; + padding: 0px; + font-size: 9px; + line-height: 1px; + text-indent: 0px; +} +.paging { + float: right; +} +.paging img, .paging select { + vertical-align: middle; +} +/* paging style 3 rules */ +.paging a:visited { + color: #212126; +} +.paging span { + font-weight: bold; +} +.noauth .loginactions { + margin-top: -25px; + text-align: right; +} +.toolbar h3.header { + float: right; + margin: 0px; + padding: 0px; +} + +/*-----------------*\ +|* global elements *| +\*-----------------*/ +.shadowtop, .shadowbottom { + height: 10px; + font-size: 1px; + display: inline-block; +/* ie/mac fix \*/ + display: block; +/* end fix */ +} +.shadowtopleft, .shadowtopright, .shadowbottomleft, .shadowbottomright { + width: 15px; + height: 10px; + font-size: 1px; +} +.shadowtop { + background: transparent url(images/shadow-top.gif) bottom repeat-x; +} +.shadowbottom { + background: url(images/shadow-bottom.gif) top left repeat-x; +} +.shadowleft { + background: url(images/shadow-left.gif) left repeat-y; +} +.shadowright { + background: url(images/shadow-right.gif) right repeat-y; +} +.shadowtopleft { + float: left; + background: url(images/shadow-topleft.gif) bottom left no-repeat; +} +.shadowtopright { + float: right; + background: url(images/shadow-topright.gif) bottom right no-repeat; +} +.shadowbottomleft { + float: left; + background: url(images/shadow-bottomleft.gif) top left no-repeat; +} +.shadowbottomright { + float: right; + background: url(images/shadow-bottomright.gif) top right no-repeat; +} +.spacer { + background: transparent url(images/row-separator.gif) repeat-x bottom left; +} +.top-spacer { + background: transparent url(images/row-separator.gif) repeat-x top left; +} + +/* This is unfortunately needed because setting a background on a does + * *not* actually affect the tr in IE - instead it just puts that background on + * each , which means the image from one may not have the right + * horizontal offset as the next . To get around this, each spacer row + * contains just one, empty, . This sucks. Patches welcome. + */ +tr.spacer { + height: 3px; + background-position: center left; +} + +/* forms */ +.row .description { + background: #e4e4e8; + color: #212126; + padding: 4px 2px; +} +.row .description .warning { + color: #8c3030; +} +.row label.name { + margin: 0px; + padding: 4px 5px 4px 25px; + border: none; + width: 130px; + float: left; + vertical-align: middle; +} +.row label.name span { + color: #8c3030; +} +.row label.name small { + font-size: 9px; +} +.row .value { + float: left; + padding: 4px; +} +.row .value input.text, .row .value input.password { + width: 150px; + font-size: 11px; +} +#cmd-search input.longtext, .row .value input.longtext { + width: 300px; +} +.row .value select.wide { + width: 300px; +} +.row .value textarea { + width: 325px; + font-size: 11px; +} +.row .value input.text.shorttext { + width: 100px; +} +.row .value input.text.tinytext { + width: 30px; +} +.row .value input, .row .value label { + vertical-align: middle; +} +.row .value textarea.user-access { + width: 400px; + height: 60px; +} +.button, .submit { + font-size: 11px; +} + +.clear:after { + height: 0px; + clear: both; + display: block; + visibility: hidden; + content: "."; +} +.clear { + display: inline-block; +} +/* start non ie/mac css \*/ +* html .clear { + height: 1%; +} +.clear { + display: block; +} +/* end non ie/mac css */ +.hide { + display: none; +} +/* Define the widths for the folder table - with table-layout: fixed, only the + * first row's widths are taken into account, NOT the table contents. */ +/* clearing class */ +.foldertab { + border-collapse: collapse; + width: 100%; + font-size: 11px; + table-layout: fixed; +} +.foldertab pre { + padding: 0px; + margin: 0px; +} +.readme { + margin: 10px 0; +} +.readme h1 { + font-size: 12px; + padding: 0; + margin: 0px; +} +* html .foldertab { + top: 0px; left: 0px; right: 0px; bottom: 0px; + width: auto; +} +.foldertab th { + padding: 3px 2px; + color: white; + background: #383741; + font-size: 11px; + text-transform: uppercase; + text-align: left; +} +.foldertab th a, .foldertab th a:hover, .foldertab th a:visited { + display: block; + width: 100%; + height: 100%; + color: #fff; + text-decoration: none; +} +.foldertab .empty { + text-align: center; + vertical-align: middle; + height: 50px; +} +.foldertab .icon, .foldertab .selectbox { + width: 18px; + text-align: center; +} +.foldertab .scrollbar { + width: 12px; +} +.foldertab .name { } +.foldertab .size, .foldertab .owner { + width: 80px; +} +.foldertab .type { + width: 120px; +} +.foldertab .modified { + width: 140px; +} +.foldertab .permission, .foldertab .action { + width: 110px; +} +.foldertab .username { + width: 150px; +} +.foldertab .email { + width: 300px; +} +.foldertab td { + padding: 1px 2px 0px 2px; + text-align: left; +} +.foldertab tr.spacer { + height: 3px; +} +.foldertab tr.symlink td, .foldertab tr.symlink a { + color: #2c9490; +} +.command pre { + margin: 5px 0 10px 0; +} +.command .prompt { + font-weight: bold; +} +.command .error { + background: #fff; + color: #bd0000; +} +#command-dialog .error { + background: none; + color: #bd0000; +} +#command-dialog .message { + background: none; + color: #2e8b57; +} +#cmd-protect input.text { + width: 80px; +} +#cmd-protect .row label.name { + width: 120px; + padding-top: 3px; +} + +/* generic modal dialog style */ +.modal-dialog { + display: none; + position: fixed; + top: 17%; + left: 50%; + border: 3px solid #3e3d45; + + +/* ie6 - doesn't support fixed positioning */ + _position: absolute; + _top: expression((document.documentElement.scrollTop || document.body.scrollTop) + Math.round(17 * (document.documentElement.offsetHeight || document.body.clientHeight) / 100) + 'px'); +} +.jqmOverlay { + background: #ddd; +} + +/* ie6 - prevent ActiveX bleed-through ( + +
      + + +
      + +
      + +
    • Shop >
    • + +
      + + + + + + +
      + +
      +
      +
      + +
      +
      +
      + +
      + + +
      +
      + +
      +
      + +
      +
      + + + +
      + +

      Latest Tech

      + + + +
      +
      +
      +
      +
      +
      + +
      +
      + +
      +
      +
      +
      +

      Inside Track

      +Indoor Training +
      +
      +
      + +
      +
      +
      +
      +
      +
      + +
      +
      + +
      +
      +
      + +

      Feature Articles

      +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      + +
      + +
      +
      + +
      + +
      +
      +
      + +
      + +
      +
      +
      + +
      +
      +

      Ventum's Metamorphosis

      + +
      +
      + +
      +
      + +
      +
      +

      BiSaddle's Factory

      + +
      +
      + +
      +
      + +
      + +
      +
      +
      + +
      +
      +

      The Gorée Project

      + +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      +
      + + + + + + + + + + + + + diff --git a/site/slowtwitch.com/www/secure/thanks.html b/site/slowtwitch.com/www/secure/thanks.html new file mode 100644 index 0000000..f7f2cef --- /dev/null +++ b/site/slowtwitch.com/www/secure/thanks.html @@ -0,0 +1,14 @@ + + + + + Thanks + + + + +
      +
      + + + diff --git a/site/slowtwitch.com/www/tick/html/black.php b/site/slowtwitch.com/www/tick/html/black.php new file mode 100644 index 0000000..8225eaa --- /dev/null +++ b/site/slowtwitch.com/www/tick/html/black.php @@ -0,0 +1,32 @@ + + + + + +:: click on the picture to close window :: + + + + +
      + + + + + + + +
      + + + +
      + +
      + + \ No newline at end of file diff --git a/site/slowtwitch.com/www/tick/html/stage.html b/site/slowtwitch.com/www/tick/html/stage.html new file mode 100644 index 0000000..a6d0030 --- /dev/null +++ b/site/slowtwitch.com/www/tick/html/stage.html @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + +
      + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
      + + + + +
      + + +  :: +
        -  + 
       
      + +
      + + + + + + + + +
      +Other races:  + +
      +
      +most recent first +
      + + + + +
      +  
      +
      +  
      +
      + + + +
      +
      + + + +
      + +
      +
      + + + +
      +
      race viewer powered by One Million Revolutions
      +
      + + + diff --git a/site/slowtwitch.com/www/tick/login.html b/site/slowtwitch.com/www/tick/login.html new file mode 100644 index 0000000..aaa474c --- /dev/null +++ b/site/slowtwitch.com/www/tick/login.html @@ -0,0 +1,33 @@ + + +One Million Revolutions Live Ticker + + + + + +
      + + + + + + + + + + + + + + + + + + +
       
      Username:
      Password:
      +
      +
      + + + diff --git a/site/slowtwitch.com/www/tick/tick.css b/site/slowtwitch.com/www/tick/tick.css new file mode 100644 index 0000000..27b4c18 --- /dev/null +++ b/site/slowtwitch.com/www/tick/tick.css @@ -0,0 +1,166 @@ + +.text +{ +font-family : Verdana, Arial, Geneva, Helvetica; +font-size : 11px; +color : #cccccc; +line-height : 120%; +} + +.whitetext +{ +font-family : Verdana, Arial, Geneva, Helvetica; +font-size : 11px; +color : #ffffff; +line-height : 120%; +} + +.smtext +{ +font-family : Verdana, Arial, Geneva, Helvetica; +font-size : 10px; +color : #888888; +line-height : 120%; +} + +.blacktext +{ +font-family : Verdana, Arial, Geneva, Helvetica; +font-size : 11px; +color : #000000; +line-height : 120%; +} + +.bannerhere +{ +font-family : Verdana, Arial, Geneva, Helvetica; +font-size : 11px; +color : #ff2222; +line-height : 120%; +font-weight : bold; +} + +.bannerthere +{ +font-family : Verdana, Arial, Geneva, Helvetica; +font-size : 11px; +color : #dddddd; +line-height : 120%; +font-weight : bold; +} + +.yellowtext +{ +font-family : Verdana, Arial, Geneva, Helvetica; +font-size : 11px; +color : #ffff66; +line-height : 120%; +} + +.yellowboldtext +{ +font-family : Verdana, Arial, Geneva, Helvetica; +font-size : 11px; +color : #ffff66; +line-height : 120%; +font-weight : bold; +} + +.boldtext +{ +font-family : Verdana, Arial, Geneva, Helvetica; +font-size : 11px; +color : #000000; +line-height : 120%; +font-weight : bold; +} + +.itext +{ +font-family : Verdana, Arial, Geneva, Helvetica; +font-size : 11px; +color : #444444; +line-height : 120%; +} + +.bigtext +{ +font-family : Verdana, Arial, Geneva, Helvetica; +font-size : 13px; +color : #333333; +line-height : 120%; +font-weight : bold; +} + +.small +{ +font-family : Verdana, Arial, Geneva, Helvetica; +font-size : 8px; +color : #000000; +} + +.notsosmall +{ +font-family : Verdana, Arial, Geneva, Helvetica; +font-size : 9px; +color : #000000; +} + +.boldnotsosmall +{ +font-family : Verdana, Arial, Geneva, Helvetica; +font-size : 9px; +color : #000000; +font-weight : bold; +} + +.redtext +{ +font-family : Verdana, Arial, Geneva, Helvetica; +font-size : 11px; +color : #ff0000; +line-height : 120%; +} + +.bigredtext +{ +font-family : Verdana, Arial, Geneva, Helvetica; +font-size : 13px; +color : #ff2222; +line-height : 120%; +} + +.blackboldtext +{ +font-family : Verdana, Arial, Geneva, Helvetica; +font-size : 11px; +color : #000000; +line-height : 120%; +font-weight : bold; +} + +.14blackboldtext +{ +font-family : Verdana, Arial, Geneva, Helvetica; +font-size : 14px; +color : #000000; +line-height : 120%; +font-weight : bold; +} + +A +{ +font-family : Verdana, Arial, Geneva, Helvetica; +font-weight : bold; +font-size : 11px; +color : #f11a07; +text-decoration : none; +} + +A:hover +{ +color : #ff4444; +font-family : Verdana, Arial, Geneva, Helvetica; +text-decoration : underline; +font-weight : bold; +} diff --git a/site/stackreach/libs/Smarty.class.php b/site/stackreach/libs/Smarty.class.php new file mode 100644 index 0000000..d174f79 --- /dev/null +++ b/site/stackreach/libs/Smarty.class.php @@ -0,0 +1,815 @@ + + * @author Uwe Tews + * @package Smarty + * @version 3.0.7 + */ + +/** + * define shorthand directory separator constant + */ +if (!defined('DS')) { + define('DS', DIRECTORY_SEPARATOR); +} + +/** + * set SMARTY_DIR to absolute path to Smarty library files. + * Sets SMARTY_DIR only if user application has not already defined it. + */ +if (!defined('SMARTY_DIR')) { + define('SMARTY_DIR', dirname(__FILE__) . DS); +} + +/** + * set SMARTY_SYSPLUGINS_DIR to absolute path to Smarty internal plugins. + * Sets SMARTY_SYSPLUGINS_DIR only if user application has not already defined it. + */ +if (!defined('SMARTY_SYSPLUGINS_DIR')) { + define('SMARTY_SYSPLUGINS_DIR', SMARTY_DIR . 'sysplugins' . DS); +} +if (!defined('SMARTY_PLUGINS_DIR')) { + define('SMARTY_PLUGINS_DIR', SMARTY_DIR . 'plugins' . DS); +} +if (!defined('SMARTY_RESOURCE_CHAR_SET')) { + define('SMARTY_RESOURCE_CHAR_SET', 'UTF-8'); +} +if (!defined('SMARTY_RESOURCE_DATE_FORMAT')) { + define('SMARTY_RESOURCE_DATE_FORMAT', '%b %e, %Y'); +} + +/** + * register the class autoloader + */ +if (!defined('SMARTY_SPL_AUTOLOAD')) { + define('SMARTY_SPL_AUTOLOAD', 0); +} + +if (SMARTY_SPL_AUTOLOAD && set_include_path(get_include_path() . PATH_SEPARATOR . SMARTY_SYSPLUGINS_DIR) !== false) { + $registeredAutoLoadFunctions = spl_autoload_functions(); + if (!isset($registeredAutoLoadFunctions['spl_autoload'])) { + spl_autoload_register(); + } +} else { + spl_autoload_register('smartyAutoload'); +} + +/** + * This is the main Smarty class + */ +class Smarty extends Smarty_Internal_Data { + /** + * constant definitions + */ + // smarty version + const SMARTY_VERSION = 'Smarty-3.0.7'; + //define variable scopes + const SCOPE_LOCAL = 0; + const SCOPE_PARENT = 1; + const SCOPE_ROOT = 2; + const SCOPE_GLOBAL = 3; + // define caching modes + const CACHING_OFF = 0; + const CACHING_LIFETIME_CURRENT = 1; + const CACHING_LIFETIME_SAVED = 2; + /** modes for handling of "" tags in templates. **/ + const PHP_PASSTHRU = 0; //-> print tags as plain text + const PHP_QUOTE = 1; //-> escape tags as entities + const PHP_REMOVE = 2; //-> escape tags as entities + const PHP_ALLOW = 3; //-> escape tags as entities + // filter types + const FILTER_POST = 'post'; + const FILTER_PRE = 'pre'; + const FILTER_OUTPUT = 'output'; + const FILTER_VARIABLE = 'variable'; + // plugin types + const PLUGIN_FUNCTION = 'function'; + const PLUGIN_BLOCK = 'block'; + const PLUGIN_COMPILER = 'compiler'; + const PLUGIN_MODIFIER = 'modifier'; + + /** + * static variables + */ + // assigned global tpl vars + static $global_tpl_vars = array(); + + /** + * variables + */ + // auto literal on delimiters with whitspace + public $auto_literal = true; + // display error on not assigned variables + public $error_unassigned = false; + // template directory + public $template_dir = null; + // default template handler + public $default_template_handler_func = null; + // compile directory + public $compile_dir = null; + // plugins directory + public $plugins_dir = null; + // cache directory + public $cache_dir = null; + // config directory + public $config_dir = null; + // force template compiling? + public $force_compile = false; + // check template for modifications? + public $compile_check = true; + // locking concurrent compiles + public $compile_locking = true; + // use sub dirs for compiled/cached files? + public $use_sub_dirs = false; + // compile_error? + public $compile_error = false; + // caching enabled + public $caching = false; + // merge compiled includes + public $merge_compiled_includes = false; + // cache lifetime + public $cache_lifetime = 3600; + // force cache file creation + public $force_cache = false; + // cache_id + public $cache_id = null; + // compile_id + public $compile_id = null; + // template delimiters + public $left_delimiter = "{"; + public $right_delimiter = "}"; + // security + public $security_class = 'Smarty_Security'; + public $security_policy = null; + public $php_handling = self::PHP_PASSTHRU; + public $allow_php_tag = true; + public $allow_php_templates = false; + public $direct_access_security = true; + public $trusted_dir = array(); + // debug mode + public $debugging = false; + public $debugging_ctrl = 'NONE'; + public $smarty_debug_id = 'SMARTY_DEBUG'; + public $debug_tpl = null; + // When set, smarty does uses this value as error_reporting-level. + public $error_reporting = null; + // config var settings + public $config_overwrite = true; //Controls whether variables with the same name overwrite each other. + public $config_booleanize = true; //Controls whether config values of on/true/yes and off/false/no get converted to boolean + public $config_read_hidden = false; //Controls whether hidden config sections/vars are read from the file. + // config vars + public $config_vars = array(); + // assigned tpl vars + public $tpl_vars = array(); + // dummy parent object + public $parent = null; + // global template functions + public $template_functions = array(); + // resource type used if none given + public $default_resource_type = 'file'; + // caching type + public $caching_type = 'file'; + // internal cache resource types + public $cache_resource_types = array('file'); + // internal config properties + public $properties = array(); + // config type + public $default_config_type = 'file'; + // cached template objects + public $template_objects = null; + // check If-Modified-Since headers + public $cache_modified_check = false; + // registered plugins + public $registered_plugins = array(); + // plugin search order + public $plugin_search_order = array('function', 'block', 'compiler', 'class'); + // registered objects + public $registered_objects = array(); + // registered classes + public $registered_classes = array(); + // registered filters + public $registered_filters = array(); + // registered resources + public $registered_resources = array(); + // autoload filter + public $autoload_filters = array(); + // status of filter on variable output + public $variable_filter = true; + // default modifier + public $default_modifiers = array(); + // global internal smarty vars + static $_smarty_vars = array(); + // start time for execution time calculation + public $start_time = 0; + // default file permissions + public $_file_perms = 0644; + // default dir permissions + public $_dir_perms = 0771; + // block tag hierarchy + public $_tag_stack = array(); + // flag if {block} tag is compiled for template inheritance + public $inheritance = false; + // generate deprecated function call notices? + public $deprecation_notices = true; + // Smarty 2 BC + public $_version = self::SMARTY_VERSION; + // self pointer to Smarty object + public $smarty; + + /** + * Class constructor, initializes basic smarty properties + */ + public function __construct() + { + // selfpointer need by some other class methods + $this->smarty = $this; + if (is_callable('mb_internal_encoding')) { + mb_internal_encoding(SMARTY_RESOURCE_CHAR_SET); + } + $this->start_time = microtime(true); + // set default dirs + $this->template_dir = array('.' . DS . 'templates' . DS); + $this->compile_dir = '.' . DS . 'templates_c' . DS; + $this->plugins_dir = array(SMARTY_PLUGINS_DIR); + $this->cache_dir = '.' . DS . 'cache' . DS; + $this->config_dir = '.' . DS . 'configs' . DS; + $this->debug_tpl = SMARTY_DIR . 'debug.tpl'; + if (isset($_SERVER['SCRIPT_NAME'])) { + $this->assignGlobal('SCRIPT_NAME', $_SERVER['SCRIPT_NAME']); + } + } + + /** + * Class destructor + */ + public function __destruct() + { + } + + /** + * fetches a rendered Smarty template + * + * @param string $template the resource handle of the template file or template object + * @param mixed $cache_id cache id to be used with this template + * @param mixed $compile_id compile id to be used with this template + * @param object $ |null $parent next higher level of Smarty variables + * @return string rendered template output + */ + public function fetch($template, $cache_id = null, $compile_id = null, $parent = null, $display = false) + { + if (!empty($cache_id) && is_object($cache_id)) { + $parent = $cache_id; + $cache_id = null; + } + if ($parent === null) { + // get default Smarty data object + $parent = $this; + } + // create template object if necessary + ($template instanceof $this->template_class)? $_template = $template : + $_template = $this->createTemplate ($template, $cache_id, $compile_id, $parent, false); + if (isset($this->error_reporting)) { + $_smarty_old_error_level = error_reporting($this->error_reporting); + } + // check URL debugging control + if (!$this->debugging && $this->debugging_ctrl == 'URL') { + if (isset($_SERVER['QUERY_STRING'])) { + $_query_string = $_SERVER['QUERY_STRING']; + } else { + $_query_string = ''; + } + if (false !== strpos($_query_string, $this->smarty_debug_id)) { + if (false !== strpos($_query_string, $this->smarty_debug_id . '=on')) { + // enable debugging for this browser session + setcookie('SMARTY_DEBUG', true); + $this->debugging = true; + } elseif (false !== strpos($_query_string, $this->smarty_debug_id . '=off')) { + // disable debugging for this browser session + setcookie('SMARTY_DEBUG', false); + $this->debugging = false; + } else { + // enable debugging for this page + $this->debugging = true; + } + } else { + if (isset($_COOKIE['SMARTY_DEBUG'])) { + $this->debugging = true; + } + } + } + // obtain data for cache modified check + if ($this->cache_modified_check && $this->caching && $display) { + $_isCached = $_template->isCached() && !$_template->has_nocache_code; + if ($_isCached) { + $_gmt_mtime = gmdate('D, d M Y H:i:s', $_template->getCachedTimestamp()) . ' GMT'; + } else { + $_gmt_mtime = ''; + } + } + // return rendered template + if ((!$this->caching || $_template->resource_object->isEvaluated) && (isset($this->autoload_filters['output']) || isset($this->registered_filters['output']))) { + $_output = Smarty_Internal_Filter_Handler::runFilter('output', $_template->getRenderedTemplate(), $_template); + } else { + $_output = $_template->getRenderedTemplate(); + } + $_template->rendered_content = null; + if (isset($this->error_reporting)) { + error_reporting($_smarty_old_error_level); + } + // display or fetch + if ($display) { + if ($this->caching && $this->cache_modified_check) { + $_last_modified_date = @substr($_SERVER['HTTP_IF_MODIFIED_SINCE'], 0, strpos($_SERVER['HTTP_IF_MODIFIED_SINCE'], 'GMT') + 3); + if ($_isCached && $_gmt_mtime == $_last_modified_date) { + if (php_sapi_name() == 'cgi') + header('Status: 304 Not Modified'); + else + header('HTTP/1.1 304 Not Modified'); + } else { + header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $_template->getCachedTimestamp()) . ' GMT'); + echo $_output; + } + } else { + echo $_output; + } + // debug output + if ($this->debugging) { + Smarty_Internal_Debug::display_debug($this); + } + return; + } else { + // return fetched content + return $_output; + } + } + + /** + * displays a Smarty template + * + * @param string $ |object $template the resource handle of the template file or template object + * @param mixed $cache_id cache id to be used with this template + * @param mixed $compile_id compile id to be used with this template + * @param object $parent next higher level of Smarty variables + */ + public function display($template, $cache_id = null, $compile_id = null, $parent = null) + { + // display template + $this->fetch ($template, $cache_id, $compile_id, $parent, true); + } + + /** + * test if cache i valid + * + * @param string $ |object $template the resource handle of the template file or template object + * @param mixed $cache_id cache id to be used with this template + * @param mixed $compile_id compile id to be used with this template + * @param object $parent next higher level of Smarty variables + * @return boolean cache status + */ + public function isCached($template, $cache_id = null, $compile_id = null, $parent = null) + { + if ($parent === null) { + $parent = $this; + } + if (!($template instanceof $this->template_class)) { + $template = $this->createTemplate ($template, $cache_id, $compile_id, $parent, false); + } + // return cache status of template + return $template->isCached(); + } + + /** + * creates a data object + * + * @param object $parent next higher level of Smarty variables + * @returns object data object + */ + public function createData($parent = null) + { + return new Smarty_Data($parent, $this); + } + + /** + * creates a template object + * + * @param string $template the resource handle of the template file + * @param mixed $cache_id cache id to be used with this template + * @param mixed $compile_id compile id to be used with this template + * @param object $parent next higher level of Smarty variables + * @param boolean $do_clone flag is Smarty object shall be cloned + * @returns object template object + */ + public function createTemplate($template, $cache_id = null, $compile_id = null, $parent = null, $do_clone = true) + { + if (!empty($cache_id) && (is_object($cache_id) || is_array($cache_id))) { + $parent = $cache_id; + $cache_id = null; + } + if (!empty($parent) && is_array($parent)) { + $data = $parent; + $parent = null; + } else { + $data = null; + } + if (!is_object($template)) { + // we got a template resource + // already in template cache? + $_templateId = sha1($template . $cache_id . $compile_id); + if (isset($this->template_objects[$_templateId]) && $this->caching) { + // return cached template object + $tpl = $this->template_objects[$_templateId]; + } else { + // create new template object + if ($do_clone) { + $tpl = new $this->template_class($template, clone $this, $parent, $cache_id, $compile_id); + } else { + $tpl = new $this->template_class($template, $this, $parent, $cache_id, $compile_id); + } + } + } else { + // just return a copy of template class + $tpl = $template; + } + // fill data if present + if (!empty($data) && is_array($data)) { + // set up variable values + foreach ($data as $_key => $_val) { + $tpl->tpl_vars[$_key] = new Smarty_variable($_val); + } + } + return $tpl; + } + + + + /** + * Check if a template resource exists + * + * @param string $resource_name template name + * @return boolean status + */ + function templateExists($resource_name) + { + // create template object + $save = $this->template_objects; + $tpl = new $this->template_class($resource_name, $this); + // check if it does exists + $result = $tpl->isExisting(); + $this->template_objects = $save; + return $result; + } + + /** + * Returns a single or all global variables + * + * @param object $smarty + * @param string $varname variable name or null + * @return string variable value or or array of variables + */ + function getGlobal($varname = null) + { + if (isset($varname)) { + if (isset(self::$global_tpl_vars[$varname])) { + return self::$global_tpl_vars[$varname]->value; + } else { + return ''; + } + } else { + $_result = array(); + foreach (self::$global_tpl_vars AS $key => $var) { + $_result[$key] = $var->value; + } + return $_result; + } + } + + /** + * Empty cache folder + * + * @param integer $exp_time expiration time + * @param string $type resource type + * @return integer number of cache files deleted + */ + function clearAllCache($exp_time = null, $type = null) + { + // load cache resource and call clearAll + return $this->loadCacheResource($type)->clearAll($exp_time); + } + + /** + * Empty cache for a specific template + * + * @param string $template_name template name + * @param string $cache_id cache id + * @param string $compile_id compile id + * @param integer $exp_time expiration time + * @param string $type resource type + * @return integer number of cache files deleted + */ + function clearCache($template_name, $cache_id = null, $compile_id = null, $exp_time = null, $type = null) + { + // load cache resource and call clear + return $this->loadCacheResource($type)->clear($template_name, $cache_id, $compile_id, $exp_time); + } + + /** + * Loads security class and enables security + */ + public function enableSecurity($security_class = null) + { + if ($security_class instanceof Smarty_Security) { + $this->security_policy = $security_class; + return; + } + if ($security_class == null) { + $security_class = $this->security_class; + } + if (class_exists($security_class)) { + $this->security_policy = new $security_class($this); + } else { + throw new SmartyException("Security class '$security_class' is not defined"); + } + } + + /** + * Disable security + */ + public function disableSecurity() + { + $this->security_policy = null; + } + + /** + * Loads cache resource. + * + * @param string $type cache resource type + * @return object of cache resource + */ + public function loadCacheResource($type = null) { + if (!isset($type)) { + $type = $this->caching_type; + } + if (in_array($type, $this->cache_resource_types)) { + $cache_resource_class = 'Smarty_Internal_CacheResource_' . ucfirst($type); + return new $cache_resource_class($this); + } + else { + // try plugins dir + $cache_resource_class = 'Smarty_CacheResource_' . ucfirst($type); + if ($this->loadPlugin($cache_resource_class)) { + return new $cache_resource_class($this); + } + else { + throw new SmartyException("Unable to load cache resource '{$type}'"); + } + } + } + + + /** + * Set template directory + * + * @param string $ |array $template_dir folder(s) of template sorces + */ + public function setTemplateDir($template_dir) + { + $this->template_dir = (array)$template_dir; + return; + } + + /** + * Adds template directory(s) to existing ones + * + * @param string $ |array $template_dir folder(s) of template sources + */ + public function addTemplateDir($template_dir) + { + $this->template_dir = array_unique(array_merge((array)$this->template_dir, (array)$template_dir)); + return; + } + + /** + * Adds directory of plugin files + * + * @param object $smarty + * @param string $ |array $ plugins folder + * @return + */ + function addPluginsDir($plugins_dir) + { + $this->plugins_dir = array_unique(array_merge((array)$this->plugins_dir, (array)$plugins_dir)); + return; + } + + + /** + * return a reference to a registered object + * + * @param string $name object name + * @return object + */ + function getRegisteredObject($name) + { + if (!isset($this->registered_objects[$name])) + throw new SmartyException("'$name' is not a registered object"); + + if (!is_object($this->registered_objects[$name][0])) + throw new SmartyException("registered '$name' is not an object"); + + return $this->registered_objects[$name][0]; + } + + + /** + * return name of debugging template + * + * @return string + */ + function getDebugTemplate() + { + return $this->debug_tpl; + } + + /** + * set the debug template + * + * @param string $tpl_name + * @return bool + */ + function setDebugTemplate($tpl_name) + { + return $this->debug_tpl = $tpl_name; + } + + /** + * Takes unknown classes and loads plugin files for them + * class name format: Smarty_PluginType_PluginName + * plugin filename format: plugintype.pluginname.php + * + * @param string $plugin_name class plugin name to load + * @return string |boolean filepath of loaded file or false + */ + public function loadPlugin($plugin_name, $check = true) + { + // if function or class exists, exit silently (already loaded) + if ($check && (is_callable($plugin_name) || class_exists($plugin_name, false))) + return true; + // Plugin name is expected to be: Smarty_[Type]_[Name] + $_plugin_name = strtolower($plugin_name); + $_name_parts = explode('_', $_plugin_name, 3); + // class name must have three parts to be valid plugin + if (count($_name_parts) < 3 || $_name_parts[0] !== 'smarty') { + throw new SmartyException("plugin {$plugin_name} is not a valid name format"); + return false; + } + // if type is "internal", get plugin from sysplugins + if ($_name_parts[1] == 'internal') { + $file = SMARTY_SYSPLUGINS_DIR . $_plugin_name . '.php'; + if (file_exists($file)) { + require_once($file); + return $file; + } else { + return false; + } + } + // plugin filename is expected to be: [type].[name].php + $_plugin_filename = "{$_name_parts[1]}.{$_name_parts[2]}.php"; + // loop through plugin dirs and find the plugin + foreach((array)$this->plugins_dir as $_plugin_dir) { + if (strpos('/\\', substr($_plugin_dir, -1)) === false) { + $_plugin_dir .= DS; + } + $file = $_plugin_dir . $_plugin_filename; + if (file_exists($file)) { + require_once($file); + return $file; + } + } + // no plugin loaded + return false; + } + + /** + * clean up properties on cloned object + */ + public function __clone() + { + // clear config vars + $this->config_vars = array(); + // clear assigned tpl vars + $this->tpl_vars = array(); + // clear objects for external methods + unset($this->register); + unset($this->filter); + } + + + /** + * Handle unknown class methods + * + * @param string $name unknown methode name + * @param array $args aurgument array + */ + public function __call($name, $args) + { + static $camel_func; + if (!isset($camel_func)) + $camel_func = create_function('$c', 'return "_" . strtolower($c[1]);'); + // see if this is a set/get for a property + $first3 = strtolower(substr($name, 0, 3)); + if (in_array($first3, array('set', 'get')) && substr($name, 3, 1) !== '_') { + // try to keep case correct for future PHP 6.0 case-sensitive class methods + // lcfirst() not available < PHP 5.3.0, so improvise + $property_name = strtolower(substr($name, 3, 1)) . substr($name, 4); + // convert camel case to underscored name + $property_name = preg_replace_callback('/([A-Z])/', $camel_func, $property_name); + if (!property_exists($this, $property_name)) { + throw new SmartyException("property '$property_name' does not exist."); + return false; + } + if ($first3 == 'get') + return $this->$property_name; + else + return $this->$property_name = $args[0]; + } + // Smarty Backward Compatible wrapper + if (strpos($name,'_') !== false) { + if (!isset($this->wrapper)) { + $this->wrapper = new Smarty_Internal_Wrapper($this); + } + return $this->wrapper->convert($name, $args); + } + // external Smarty methods ? + foreach(array('filter','register') as $external) { + if (method_exists("Smarty_Internal_{$external}",$name)) { + if (!isset($this->$external)) { + $class = "Smarty_Internal_{$external}"; + $this->$external = new $class($this); + } + return call_user_func_array(array($this->$external,$name), $args); + } + } + if (in_array($name,array('clearCompiledTemplate','compileAllTemplates','compileAllConfig','testInstall','getTags'))) { + if (!isset($this->utility)) { + $this->utility = new Smarty_Internal_Utility($this); + } + return call_user_func_array(array($this->utility,$name), $args); + } + // PHP4 call to constructor? + if (strtolower($name) == 'smarty') { + throw new SmartyException('Please use parent::__construct() to call parent constuctor'); + return false; + } + throw new SmartyException("Call of unknown function '$name'."); + } +} + +/** + * Autoloader + */ +function smartyAutoload($class) +{ + $_class = strtolower($class); + if (substr($_class, 0, 16) === 'smarty_internal_' || $_class == 'smarty_security') { + include SMARTY_SYSPLUGINS_DIR . $_class . '.php'; + } +} + +/** + * Smarty exception class + */ +Class SmartyException extends Exception { +} + +/** + * Smarty compiler exception class + */ +Class SmartyCompilerException extends SmartyException { +} + +?> diff --git a/site/stackreach/libs/debug.tpl b/site/stackreach/libs/debug.tpl new file mode 100644 index 0000000..058c5b2 --- /dev/null +++ b/site/stackreach/libs/debug.tpl @@ -0,0 +1,133 @@ +{capture name='_smarty_debug' assign=debug_output} + + + + Smarty Debug Console + + + + +

      Smarty Debug Console - {if isset($template_name)}{$template_name|debug_print_var}{else}Total Time {$execution_time|string_format:"%.5f"}{/if}

      + +{if !empty($template_data)} +

      included templates & config files (load time in seconds)

      + +
      +{foreach $template_data as $template} + {$template.name} + + (compile {$template['compile_time']|string_format:"%.5f"}) (render {$template['render_time']|string_format:"%.5f"}) (cache {$template['cache_time']|string_format:"%.5f"}) + +
      +{/foreach} +
      +{/if} + +

      assigned template variables

      + + + {foreach $assigned_vars as $vars} + + + + {/foreach} +
      ${$vars@key|escape:'html'}{$vars|debug_print_var}
      + +

      assigned config file variables (outer template scope)

      + + + {foreach $config_vars as $vars} + + + + {/foreach} + +
      {$vars@key|escape:'html'}{$vars|debug_print_var}
      + + +{/capture} + diff --git a/site/stackreach/libs/plugins/block.php.php b/site/stackreach/libs/plugins/block.php.php new file mode 100644 index 0000000..8fedd8b --- /dev/null +++ b/site/stackreach/libs/plugins/block.php.php @@ -0,0 +1,27 @@ +allow_php_tag) { + throw new SmartyException("{php} is deprecated, set allow_php_tag = true to enable"); + } + eval($content); + return ''; +} + +?> \ No newline at end of file diff --git a/site/stackreach/libs/plugins/block.textformat.php b/site/stackreach/libs/plugins/block.textformat.php new file mode 100644 index 0000000..517fd62 --- /dev/null +++ b/site/stackreach/libs/plugins/block.textformat.php @@ -0,0 +1,102 @@ + + * Name: textformat
      + * Purpose: format text a certain way with preset styles + * or custom wrap/indent settings
      + * + * @link http://smarty.php.net/manual/en/language.function.textformat.php {textformat} + * (Smarty online manual) + * @param array $params parameters + *
      + * Params:   style: string (email)
      + *            indent: integer (0)
      + *            wrap: integer (80)
      + *            wrap_char string ("\n")
      + *            indent_char: string (" ")
      + *            wrap_boundary: boolean (true)
      + * 
      + * @author Monte Ohrt + * @param string $content contents of the block + * @param object $template template object + * @param boolean &$repeat repeat flag + * @return string content re-formatted + */ +function smarty_block_textformat($params, $content, $template, &$repeat) +{ + if (is_null($content)) { + return; + } + + $style = null; + $indent = 0; + $indent_first = 0; + $indent_char = ' '; + $wrap = 80; + $wrap_char = "\n"; + $wrap_cut = false; + $assign = null; + + foreach ($params as $_key => $_val) { + switch ($_key) { + case 'style': + case 'indent_char': + case 'wrap_char': + case 'assign': + $$_key = (string)$_val; + break; + + case 'indent': + case 'indent_first': + case 'wrap': + $$_key = (int)$_val; + break; + + case 'wrap_cut': + $$_key = (bool)$_val; + break; + + default: + trigger_error("textformat: unknown attribute '$_key'"); + } + } + + if ($style == 'email') { + $wrap = 72; + } + // split into paragraphs + $_paragraphs = preg_split('![\r\n][\r\n]!', $content); + $_output = ''; + + for($_x = 0, $_y = count($_paragraphs); $_x < $_y; $_x++) { + if ($_paragraphs[$_x] == '') { + continue; + } + // convert mult. spaces & special chars to single space + $_paragraphs[$_x] = preg_replace(array('!\s+!', '!(^\s+)|(\s+$)!'), array(' ', ''), $_paragraphs[$_x]); + // indent first line + if ($indent_first > 0) { + $_paragraphs[$_x] = str_repeat($indent_char, $indent_first) . $_paragraphs[$_x]; + } + // wordwrap sentences + $_paragraphs[$_x] = wordwrap($_paragraphs[$_x], $wrap - $indent, $wrap_char, $wrap_cut); + // indent lines + if ($indent > 0) { + $_paragraphs[$_x] = preg_replace('!^!m', str_repeat($indent_char, $indent), $_paragraphs[$_x]); + } + } + $_output = implode($wrap_char . $wrap_char, $_paragraphs); + + return $assign ? $template->assign($assign, $_output) : $_output; +} + +?> \ No newline at end of file diff --git a/site/stackreach/libs/plugins/function.counter.php b/site/stackreach/libs/plugins/function.counter.php new file mode 100644 index 0000000..7c50bd4 --- /dev/null +++ b/site/stackreach/libs/plugins/function.counter.php @@ -0,0 +1,78 @@ + + * Name: counter
      + * Purpose: print out a counter value + * @author Monte Ohrt + * @link http://smarty.php.net/manual/en/language.function.counter.php {counter} + * (Smarty online manual) + * @param array parameters + * @param Smarty + * @param object $template template object + * @return string|null + */ +function smarty_function_counter($params, $template) +{ + static $counters = array(); + + $name = (isset($params['name'])) ? $params['name'] : 'default'; + if (!isset($counters[$name])) { + $counters[$name] = array( + 'start'=>1, + 'skip'=>1, + 'direction'=>'up', + 'count'=>1 + ); + } + $counter =& $counters[$name]; + + if (isset($params['start'])) { + $counter['start'] = $counter['count'] = (int)$params['start']; + } + + if (!empty($params['assign'])) { + $counter['assign'] = $params['assign']; + } + + if (isset($counter['assign'])) { + $template->assign($counter['assign'], $counter['count']); + } + + if (isset($params['print'])) { + $print = (bool)$params['print']; + } else { + $print = empty($counter['assign']); + } + + if ($print) { + $retval = $counter['count']; + } else { + $retval = null; + } + + if (isset($params['skip'])) { + $counter['skip'] = $params['skip']; + } + + if (isset($params['direction'])) { + $counter['direction'] = $params['direction']; + } + + if ($counter['direction'] == "down") + $counter['count'] -= $counter['skip']; + else + $counter['count'] += $counter['skip']; + + return $retval; + +} + +?> \ No newline at end of file diff --git a/site/stackreach/libs/plugins/function.cycle.php b/site/stackreach/libs/plugins/function.cycle.php new file mode 100644 index 0000000..98e3e28 --- /dev/null +++ b/site/stackreach/libs/plugins/function.cycle.php @@ -0,0 +1,106 @@ + + * Name: cycle
      + * Date: May 3, 2002
      + * Purpose: cycle through given values
      + * Input: + * - name = name of cycle (optional) + * - values = comma separated list of values to cycle, + * or an array of values to cycle + * (this can be left out for subsequent calls) + * - reset = boolean - resets given var to true + * - print = boolean - print var or not. default is true + * - advance = boolean - whether or not to advance the cycle + * - delimiter = the value delimiter, default is "," + * - assign = boolean, assigns to template var instead of + * printed. + * + * Examples:
      + *
      + * {cycle values="#eeeeee,#d0d0d0d"}
      + * {cycle name=row values="one,two,three" reset=true}
      + * {cycle name=row}
      + * 
      + * @link http://smarty.php.net/manual/en/language.function.cycle.php {cycle} + * (Smarty online manual) + * @author Monte Ohrt + * @author credit to Mark Priatel + * @author credit to Gerard + * @author credit to Jason Sweat + * @version 1.3 + * @param array + * @param object $template template object + * @return string|null + */ + +function smarty_function_cycle($params, $template) +{ + static $cycle_vars; + + $name = (empty($params['name'])) ? 'default' : $params['name']; + $print = (isset($params['print'])) ? (bool)$params['print'] : true; + $advance = (isset($params['advance'])) ? (bool)$params['advance'] : true; + $reset = (isset($params['reset'])) ? (bool)$params['reset'] : false; + + if (!in_array('values', array_keys($params))) { + if(!isset($cycle_vars[$name]['values'])) { + trigger_error("cycle: missing 'values' parameter"); + return; + } + } else { + if(isset($cycle_vars[$name]['values']) + && $cycle_vars[$name]['values'] != $params['values'] ) { + $cycle_vars[$name]['index'] = 0; + } + $cycle_vars[$name]['values'] = $params['values']; + } + + if (isset($params['delimiter'])) { + $cycle_vars[$name]['delimiter'] = $params['delimiter']; + } elseif (!isset($cycle_vars[$name]['delimiter'])) { + $cycle_vars[$name]['delimiter'] = ','; + } + + if(is_array($cycle_vars[$name]['values'])) { + $cycle_array = $cycle_vars[$name]['values']; + } else { + $cycle_array = explode($cycle_vars[$name]['delimiter'],$cycle_vars[$name]['values']); + } + + if(!isset($cycle_vars[$name]['index']) || $reset ) { + $cycle_vars[$name]['index'] = 0; + } + + if (isset($params['assign'])) { + $print = false; + $template->assign($params['assign'], $cycle_array[$cycle_vars[$name]['index']]); + } + + if($print) { + $retval = $cycle_array[$cycle_vars[$name]['index']]; + } else { + $retval = null; + } + + if($advance) { + if ( $cycle_vars[$name]['index'] >= count($cycle_array) -1 ) { + $cycle_vars[$name]['index'] = 0; + } else { + $cycle_vars[$name]['index']++; + } + } + + return $retval; +} + +?> \ No newline at end of file diff --git a/site/stackreach/libs/plugins/function.fetch.php b/site/stackreach/libs/plugins/function.fetch.php new file mode 100644 index 0000000..2b09fb9 --- /dev/null +++ b/site/stackreach/libs/plugins/function.fetch.php @@ -0,0 +1,216 @@ + + * Name: fetch
      + * Purpose: fetch file, web or ftp data and display results + * @link http://smarty.php.net/manual/en/language.function.fetch.php {fetch} + * (Smarty online manual) + * @author Monte Ohrt + * @param array $params parameters + * @param object $template template object + * @return string|null if the assign parameter is passed, Smarty assigns the + * result to a template variable + */ +function smarty_function_fetch($params, $template) +{ + if (empty($params['file'])) { + trigger_error("[plugin] fetch parameter 'file' cannot be empty",E_USER_NOTICE); + return; + } + + $content = ''; + if (isset($template->security_policy) && !preg_match('!^(http|ftp)://!i', $params['file'])) { + if(!$template->security_policy->isTrustedResourceDir($params['file'])) { + return; + } + + // fetch the file + if($fp = @fopen($params['file'],'r')) { + while(!feof($fp)) { + $content .= fgets ($fp,4096); + } + fclose($fp); + } else { + trigger_error('[plugin] fetch cannot read file \'' . $params['file'] . '\'',E_USER_NOTICE); + return; + } + } else { + // not a local file + if(preg_match('!^http://!i',$params['file'])) { + // http fetch + if($uri_parts = parse_url($params['file'])) { + // set defaults + $host = $server_name = $uri_parts['host']; + $timeout = 30; + $accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*"; + $agent = "Smarty Template Engine ".$template->_version; + $referer = ""; + $uri = !empty($uri_parts['path']) ? $uri_parts['path'] : '/'; + $uri .= !empty($uri_parts['query']) ? '?' . $uri_parts['query'] : ''; + $_is_proxy = false; + if(empty($uri_parts['port'])) { + $port = 80; + } else { + $port = $uri_parts['port']; + } + if(!empty($uri_parts['user'])) { + $user = $uri_parts['user']; + } + if(!empty($uri_parts['pass'])) { + $pass = $uri_parts['pass']; + } + // loop through parameters, setup headers + foreach($params as $param_key => $param_value) { + switch($param_key) { + case "file": + case "assign": + case "assign_headers": + break; + case "user": + if(!empty($param_value)) { + $user = $param_value; + } + break; + case "pass": + if(!empty($param_value)) { + $pass = $param_value; + } + break; + case "accept": + if(!empty($param_value)) { + $accept = $param_value; + } + break; + case "header": + if(!empty($param_value)) { + if(!preg_match('![\w\d-]+: .+!',$param_value)) { + trigger_error("[plugin] invalid header format '".$param_value."'",E_USER_NOTICE); + return; + } else { + $extra_headers[] = $param_value; + } + } + break; + case "proxy_host": + if(!empty($param_value)) { + $proxy_host = $param_value; + } + break; + case "proxy_port": + if(!preg_match('!\D!', $param_value)) { + $proxy_port = (int) $param_value; + } else { + trigger_error("[plugin] invalid value for attribute '".$param_key."'",E_USER_NOTICE); + return; + } + break; + case "agent": + if(!empty($param_value)) { + $agent = $param_value; + } + break; + case "referer": + if(!empty($param_value)) { + $referer = $param_value; + } + break; + case "timeout": + if(!preg_match('!\D!', $param_value)) { + $timeout = (int) $param_value; + } else { + trigger_error("[plugin] invalid value for attribute '".$param_key."'",E_USER_NOTICE); + return; + } + break; + default: + trigger_error("[plugin] unrecognized attribute '".$param_key."'",E_USER_NOTICE); + return; + } + } + if(!empty($proxy_host) && !empty($proxy_port)) { + $_is_proxy = true; + $fp = fsockopen($proxy_host,$proxy_port,$errno,$errstr,$timeout); + } else { + $fp = fsockopen($server_name,$port,$errno,$errstr,$timeout); + } + + if(!$fp) { + trigger_error("[plugin] unable to fetch: $errstr ($errno)",E_USER_NOTICE); + return; + } else { + if($_is_proxy) { + fputs($fp, 'GET ' . $params['file'] . " HTTP/1.0\r\n"); + } else { + fputs($fp, "GET $uri HTTP/1.0\r\n"); + } + if(!empty($host)) { + fputs($fp, "Host: $host\r\n"); + } + if(!empty($accept)) { + fputs($fp, "Accept: $accept\r\n"); + } + if(!empty($agent)) { + fputs($fp, "User-Agent: $agent\r\n"); + } + if(!empty($referer)) { + fputs($fp, "Referer: $referer\r\n"); + } + if(isset($extra_headers) && is_array($extra_headers)) { + foreach($extra_headers as $curr_header) { + fputs($fp, $curr_header."\r\n"); + } + } + if(!empty($user) && !empty($pass)) { + fputs($fp, "Authorization: BASIC ".base64_encode("$user:$pass")."\r\n"); + } + + fputs($fp, "\r\n"); + while(!feof($fp)) { + $content .= fgets($fp,4096); + } + fclose($fp); + $csplit = preg_split("!\r\n\r\n!",$content,2); + + $content = $csplit[1]; + + if(!empty($params['assign_headers'])) { + $template->assign($params['assign_headers'],preg_split("!\r\n!",$csplit[0])); + } + } + } else { + trigger_error("[plugin fetch] unable to parse URL, check syntax",E_USER_NOTICE); + return; + } + } else { + // ftp fetch + if($fp = @fopen($params['file'],'r')) { + while(!feof($fp)) { + $content .= fgets ($fp,4096); + } + fclose($fp); + } else { + trigger_error('[plugin] fetch cannot read file \'' . $params['file'] .'\'',E_USER_NOTICE); + return; + } + } + + } + + + if (!empty($params['assign'])) { + $template->assign($params['assign'],$content); + } else { + return $content; + } +} + +?> \ No newline at end of file diff --git a/site/stackreach/libs/plugins/function.html_checkboxes.php b/site/stackreach/libs/plugins/function.html_checkboxes.php new file mode 100644 index 0000000..6a1a3ff --- /dev/null +++ b/site/stackreach/libs/plugins/function.html_checkboxes.php @@ -0,0 +1,143 @@ + + * Type: function
      + * Name: html_checkboxes
      + * Date: 24.Feb.2003
      + * Purpose: Prints out a list of checkbox input types
      + * Examples: + *
      + * {html_checkboxes values=$ids output=$names}
      + * {html_checkboxes values=$ids name='box' separator='
      ' output=$names} + * {html_checkboxes values=$ids checked=$checked separator='
      ' output=$names} + *
      + * @link http://smarty.php.net/manual/en/language.function.html.checkboxes.php {html_checkboxes} + * (Smarty online manual) + * @author Christopher Kvarme + * @author credits to Monte Ohrt + * @version 1.0 + * @param array $params parameters + * Input:
      + * - name (optional) - string default "checkbox" + * - values (required) - array + * - options (optional) - associative array + * - checked (optional) - array default not set + * - separator (optional) - ie
      or   + * - output (optional) - the output next to each checkbox + * - assign (optional) - assign the output as an array to this variable + * @param object $template template object + * @return string + * @uses smarty_function_escape_special_chars() + */ +function smarty_function_html_checkboxes($params, $template) +{ + require_once(SMARTY_PLUGINS_DIR . 'shared.escape_special_chars.php'); + + $name = 'checkbox'; + $values = null; + $options = null; + $selected = null; + $separator = ''; + $labels = true; + $output = null; + + $extra = ''; + + foreach($params as $_key => $_val) { + switch($_key) { + case 'name': + case 'separator': + $$_key = $_val; + break; + + case 'labels': + $$_key = (bool)$_val; + break; + + case 'options': + $$_key = (array)$_val; + break; + + case 'values': + case 'output': + $$_key = array_values((array)$_val); + break; + + case 'checked': + case 'selected': + $selected = array_map('strval', array_values((array)$_val)); + break; + + case 'checkboxes': + trigger_error('html_checkboxes: the use of the "checkboxes" attribute is deprecated, use "options" instead', E_USER_WARNING); + $options = (array)$_val; + break; + + case 'assign': + break; + + default: + if(!is_array($_val)) { + $extra .= ' '.$_key.'="'.smarty_function_escape_special_chars($_val).'"'; + } else { + trigger_error("html_checkboxes: extra attribute '$_key' cannot be an array", E_USER_NOTICE); + } + break; + } + } + + if (!isset($options) && !isset($values)) + return ''; /* raise error here? */ + + settype($selected, 'array'); + $_html_result = array(); + + if (isset($options)) { + + foreach ($options as $_key=>$_val) + $_html_result[] = smarty_function_html_checkboxes_output($name, $_key, $_val, $selected, $extra, $separator, $labels); + + + } else { + foreach ($values as $_i=>$_key) { + $_val = isset($output[$_i]) ? $output[$_i] : ''; + $_html_result[] = smarty_function_html_checkboxes_output($name, $_key, $_val, $selected, $extra, $separator, $labels); + } + + } + + if(!empty($params['assign'])) { + $template->assign($params['assign'], $_html_result); + } else { + return implode("\n",$_html_result); + } + +} + +function smarty_function_html_checkboxes_output($name, $value, $output, $selected, $extra, $separator, $labels) { + $_output = ''; + if ($labels) $_output .= ''; + $_output .= $separator; + + return $_output; +} + +?> \ No newline at end of file diff --git a/site/stackreach/libs/plugins/function.html_image.php b/site/stackreach/libs/plugins/function.html_image.php new file mode 100644 index 0000000..abb7b57 --- /dev/null +++ b/site/stackreach/libs/plugins/function.html_image.php @@ -0,0 +1,137 @@ + + * Name: html_image
      + * Date: Feb 24, 2003
      + * Purpose: format HTML tags for the image
      + * Examples: {html_image file="/images/masthead.gif"} + * Output: + * + * @link http://smarty.php.net/manual/en/language.function.html.image.php {html_image} + * (Smarty online manual) + * @author Monte Ohrt + * @author credits to Duda + * @version 1.0 + * @param array $params parameters + * Input:
      + * - file = file (and path) of image (required) + * - height = image height (optional, default actual height) + * - width = image width (optional, default actual width) + * - basedir = base directory for absolute paths, default + * is environment variable DOCUMENT_ROOT + * - path_prefix = prefix for path output (optional, default empty) + * @param object $template template object + * @return string + * @uses smarty_function_escape_special_chars() + */ +function smarty_function_html_image($params, $template) +{ + require_once(SMARTY_PLUGINS_DIR . 'shared.escape_special_chars.php'); + + $alt = ''; + $file = ''; + $height = ''; + $width = ''; + $extra = ''; + $prefix = ''; + $suffix = ''; + $path_prefix = ''; + $server_vars = $_SERVER; + $basedir = isset($server_vars['DOCUMENT_ROOT']) ? $server_vars['DOCUMENT_ROOT'] : ''; + foreach($params as $_key => $_val) { + switch ($_key) { + case 'file': + case 'height': + case 'width': + case 'dpi': + case 'path_prefix': + case 'basedir': + $$_key = $_val; + break; + + case 'alt': + if (!is_array($_val)) { + $$_key = smarty_function_escape_special_chars($_val); + } else { + throw new SmartyException ("html_image: extra attribute '$_key' cannot be an array", E_USER_NOTICE); + } + break; + + case 'link': + case 'href': + $prefix = ''; + $suffix = ''; + break; + + default: + if (!is_array($_val)) { + $extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_val) . '"'; + } else { + throw new SmartyException ("html_image: extra attribute '$_key' cannot be an array", E_USER_NOTICE); + } + break; + } + } + + if (empty($file)) { + trigger_error("html_image: missing 'file' parameter", E_USER_NOTICE); + return; + } + + if (substr($file, 0, 1) == '/') { + $_image_path = $basedir . $file; + } else { + $_image_path = $file; + } + + if (!isset($params['width']) || !isset($params['height'])) { + if (!$_image_data = @getimagesize($_image_path)) { + if (!file_exists($_image_path)) { + trigger_error("html_image: unable to find '$_image_path'", E_USER_NOTICE); + return; + } else if (!is_readable($_image_path)) { + trigger_error("html_image: unable to read '$_image_path'", E_USER_NOTICE); + return; + } else { + trigger_error("html_image: '$_image_path' is not a valid image file", E_USER_NOTICE); + return; + } + } + if (isset($template->security_policy)) { + if (!$template->security_policy->isTrustedResourceDir($_image_path)) { + return; + } + } + + if (!isset($params['width'])) { + $width = $_image_data[0]; + } + if (!isset($params['height'])) { + $height = $_image_data[1]; + } + } + + if (isset($params['dpi'])) { + if (strstr($server_vars['HTTP_USER_AGENT'], 'Mac')) { + $dpi_default = 72; + } else { + $dpi_default = 96; + } + $_resize = $dpi_default / $params['dpi']; + $width = round($width * $_resize); + $height = round($height * $_resize); + } + + return $prefix . '' . $alt . '' . $suffix; +} + +?> \ No newline at end of file diff --git a/site/stackreach/libs/plugins/function.html_options.php b/site/stackreach/libs/plugins/function.html_options.php new file mode 100644 index 0000000..7ac0390 --- /dev/null +++ b/site/stackreach/libs/plugins/function.html_options.php @@ -0,0 +1,133 @@ + + * Name: html_options
      + * Purpose: Prints the list of