<!--{{{-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml' />
<!--}}}-->
Background: #fff
Foreground: #000
PrimaryPale: #8cf
PrimaryLight: #18f
PrimaryMid: #04b
PrimaryDark: #014
SecondaryPale: #ffc
SecondaryLight: #fe8
SecondaryMid: #db4
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
/*{{{*/
body {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}

a {color:[[ColorPalette::PrimaryMid]];}
a:hover {background-color:[[ColorPalette::PrimaryMid]]; color:[[ColorPalette::Background]];}
a img {border:0;}

h1,h2,h3,h4,h5,h6 {color:[[ColorPalette::SecondaryDark]]; background:transparent;}
h1 {border-bottom:2px solid [[ColorPalette::TertiaryLight]];}
h2,h3 {border-bottom:1px solid [[ColorPalette::TertiaryLight]];}

.button {color:[[ColorPalette::PrimaryDark]]; border:1px solid [[ColorPalette::Background]];}
.button:hover {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::SecondaryLight]]; border-color:[[ColorPalette::SecondaryMid]];}
.button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::SecondaryDark]];}

.header {background:[[ColorPalette::PrimaryMid]];}
.headerShadow {color:[[ColorPalette::Foreground]];}
.headerShadow a {font-weight:normal; color:[[ColorPalette::Foreground]];}
.headerForeground {color:[[ColorPalette::Background]];}
.headerForeground a {font-weight:normal; color:[[ColorPalette::PrimaryPale]];}

.tabSelected{color:[[ColorPalette::PrimaryDark]];
	background:[[ColorPalette::TertiaryPale]];
	border-left:1px solid [[ColorPalette::TertiaryLight]];
	border-top:1px solid [[ColorPalette::TertiaryLight]];
	border-right:1px solid [[ColorPalette::TertiaryLight]];
}
.tabUnselected {color:[[ColorPalette::Background]]; background:[[ColorPalette::TertiaryMid]];}
.tabContents {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::TertiaryPale]]; border:1px solid [[ColorPalette::TertiaryLight]];}
.tabContents .button {border:0;}

#sidebar {}
#sidebarOptions input {border:1px solid [[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel {background:[[ColorPalette::PrimaryPale]];}
#sidebarOptions .sliderPanel a {border:none;color:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:hover {color:[[ColorPalette::Background]]; background:[[ColorPalette::PrimaryMid]];}
#sidebarOptions .sliderPanel a:active {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::Background]];}

.wizard {background:[[ColorPalette::PrimaryPale]]; border:1px solid [[ColorPalette::PrimaryMid]];}
.wizard h1 {color:[[ColorPalette::PrimaryDark]]; border:none;}
.wizard h2 {color:[[ColorPalette::Foreground]]; border:none;}
.wizardStep {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];
	border:1px solid [[ColorPalette::PrimaryMid]];}
.wizardStep.wizardStepDone {background:[[ColorPalette::TertiaryLight]];}
.wizardFooter {background:[[ColorPalette::PrimaryPale]];}
.wizardFooter .status {background:[[ColorPalette::PrimaryDark]]; color:[[ColorPalette::Background]];}
.wizard .button {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryLight]]; border: 1px solid;
	border-color:[[ColorPalette::SecondaryPale]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryDark]] [[ColorPalette::SecondaryPale]];}
.wizard .button:hover {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Background]];}
.wizard .button:active {color:[[ColorPalette::Background]]; background:[[ColorPalette::Foreground]]; border: 1px solid;
	border-color:[[ColorPalette::PrimaryDark]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryPale]] [[ColorPalette::PrimaryDark]];}
	
.wizard .notChanged {background:transparent;}
.wizard .changedLocally {background:#80ff80;}
.wizard .changedServer {background:#8080ff;}
.wizard .changedBoth {background:#ff8080;}
.wizard .notFound {background:#ffff80;}
.wizard .putToServer {background:#ff80ff;}
.wizard .gotFromServer {background:#80ffff;}

#messageArea {border:1px solid [[ColorPalette::SecondaryMid]]; background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]];}
#messageArea .button {color:[[ColorPalette::PrimaryMid]]; background:[[ColorPalette::SecondaryPale]]; border:none;}

.popupTiddler {background:[[ColorPalette::TertiaryPale]]; border:2px solid [[ColorPalette::TertiaryMid]];}

.popup {background:[[ColorPalette::TertiaryPale]]; color:[[ColorPalette::TertiaryDark]]; border-left:1px solid [[ColorPalette::TertiaryMid]]; border-top:1px solid [[ColorPalette::TertiaryMid]]; border-right:2px solid [[ColorPalette::TertiaryDark]]; border-bottom:2px solid [[ColorPalette::TertiaryDark]];}
.popup hr {color:[[ColorPalette::PrimaryDark]]; background:[[ColorPalette::PrimaryDark]]; border-bottom:1px;}
.popup li.disabled {color:[[ColorPalette::TertiaryMid]];}
.popup li a, .popup li a:visited {color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border: none;}
.popup li a:active {background:[[ColorPalette::SecondaryPale]]; color:[[ColorPalette::Foreground]]; border: none;}
.popupHighlight {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
.listBreak div {border-bottom:1px solid [[ColorPalette::TertiaryDark]];}

.tiddler .defaultCommand {font-weight:bold;}

.shadow .title {color:[[ColorPalette::TertiaryDark]];}

.title {color:[[ColorPalette::SecondaryDark]];}
.subtitle {color:[[ColorPalette::TertiaryDark]];}

.toolbar {color:[[ColorPalette::PrimaryMid]];}
.toolbar a {color:[[ColorPalette::TertiaryLight]];}
.selected .toolbar a {color:[[ColorPalette::TertiaryMid]];}
.selected .toolbar a:hover {color:[[ColorPalette::Foreground]];}

.tagging, .tagged {border:1px solid [[ColorPalette::TertiaryPale]]; background-color:[[ColorPalette::TertiaryPale]];}
.selected .tagging, .selected .tagged {background-color:[[ColorPalette::TertiaryLight]]; border:1px solid [[ColorPalette::TertiaryMid]];}
.tagging .listTitle, .tagged .listTitle {color:[[ColorPalette::PrimaryDark]];}
.tagging .button, .tagged .button {border:none;}

.footer {color:[[ColorPalette::TertiaryLight]];}
.selected .footer {color:[[ColorPalette::TertiaryMid]];}

.sparkline {background:[[ColorPalette::PrimaryPale]]; border:0;}
.sparktick {background:[[ColorPalette::PrimaryDark]];}

.error, .errorButton {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::Error]];}
.warning {color:[[ColorPalette::Foreground]]; background:[[ColorPalette::SecondaryPale]];}
.lowlight {background:[[ColorPalette::TertiaryLight]];}

.zoomer {background:none; color:[[ColorPalette::TertiaryMid]]; border:3px solid [[ColorPalette::TertiaryMid]];}

.imageLink, #displayArea .imageLink {background:transparent;}

.annotation {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; border:2px solid [[ColorPalette::SecondaryMid]];}

.viewer .listTitle {list-style-type:none; margin-left:-2em;}
.viewer .button {border:1px solid [[ColorPalette::SecondaryMid]];}
.viewer blockquote {border-left:3px solid [[ColorPalette::TertiaryDark]];}

.viewer table, table.twtable {border:2px solid [[ColorPalette::TertiaryDark]];}
.viewer th, .viewer thead td, .twtable th, .twtable thead td {background:[[ColorPalette::SecondaryMid]]; border:1px solid [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::Background]];}
.viewer td, .viewer tr, .twtable td, .twtable tr {border:1px solid [[ColorPalette::TertiaryDark]];}

.viewer pre {border:1px solid [[ColorPalette::SecondaryLight]]; background:[[ColorPalette::SecondaryPale]];}
.viewer code {color:[[ColorPalette::SecondaryDark]];}
.viewer hr {border:0; border-top:dashed 1px [[ColorPalette::TertiaryDark]]; color:[[ColorPalette::TertiaryDark]];}

.highlight, .marked {background:[[ColorPalette::SecondaryLight]];}

.editor input {border:1px solid [[ColorPalette::PrimaryMid]];}
.editor textarea {border:1px solid [[ColorPalette::PrimaryMid]]; width:100%;}
.editorFooter {color:[[ColorPalette::TertiaryMid]];}

#backstageArea {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::TertiaryMid]];}
#backstageArea a {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstageArea a:hover {background:[[ColorPalette::SecondaryLight]]; color:[[ColorPalette::Foreground]]; }
#backstageArea a.backstageSelTab {background:[[ColorPalette::Background]]; color:[[ColorPalette::Foreground]];}
#backstageButton a {background:none; color:[[ColorPalette::Background]]; border:none;}
#backstageButton a:hover {background:[[ColorPalette::Foreground]]; color:[[ColorPalette::Background]]; border:none;}
#backstagePanel {background:[[ColorPalette::Background]]; border-color: [[ColorPalette::Background]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]] [[ColorPalette::TertiaryDark]];}
.backstagePanelFooter .button {border:none; color:[[ColorPalette::Background]];}
.backstagePanelFooter .button:hover {color:[[ColorPalette::Foreground]];}
#backstageCloak {background:[[ColorPalette::Foreground]]; opacity:0.6; filter:'alpha(opacity:60)';}
/*}}}*/
/*{{{*/
* html .tiddler {height:1%;}

body {font-size:.75em; font-family:arial,helvetica; margin:0; padding:0;}

h1,h2,h3,h4,h5,h6 {font-weight:bold; text-decoration:none;}
h1,h2,h3 {padding-bottom:1px; margin-top:1.2em;margin-bottom:0.3em;}
h4,h5,h6 {margin-top:1em;}
h1 {font-size:1.35em;}
h2 {font-size:1.25em;}
h3 {font-size:1.1em;}
h4 {font-size:1em;}
h5 {font-size:.9em;}

hr {height:1px;}

a {text-decoration:none;}

dt {font-weight:bold;}

ol {list-style-type:decimal;}
ol ol {list-style-type:lower-alpha;}
ol ol ol {list-style-type:lower-roman;}
ol ol ol ol {list-style-type:decimal;}
ol ol ol ol ol {list-style-type:lower-alpha;}
ol ol ol ol ol ol {list-style-type:lower-roman;}
ol ol ol ol ol ol ol {list-style-type:decimal;}

.txtOptionInput {width:11em;}

#contentWrapper .chkOptionInput {border:0;}

.externalLink {text-decoration:underline;}

.indent {margin-left:3em;}
.outdent {margin-left:3em; text-indent:-3em;}
code.escaped {white-space:nowrap;}

.tiddlyLinkExisting {font-weight:bold;}
.tiddlyLinkNonExisting {font-style:italic;}

/* the 'a' is required for IE, otherwise it renders the whole tiddler in bold */
a.tiddlyLinkNonExisting.shadow {font-weight:bold;}

#mainMenu .tiddlyLinkExisting,
	#mainMenu .tiddlyLinkNonExisting,
	#sidebarTabs .tiddlyLinkNonExisting {font-weight:normal; font-style:normal;}
#sidebarTabs .tiddlyLinkExisting {font-weight:bold; font-style:normal;}

.header {position:relative;}
.header a:hover {background:transparent;}
.headerShadow {position:relative; padding:4.5em 0em 1em 1em; left:-1px; top:-1px;}
.headerForeground {position:absolute; padding:4.5em 0em 1em 1em; left:0px; top:0px;}

.siteTitle {font-size:3em;}
.siteSubtitle {font-size:1.2em;}

#mainMenu {position:absolute; left:0; width:10em; text-align:right; line-height:1.6em; padding:1.5em 0.5em 0.5em 0.5em; font-size:1.1em;}

#sidebar {position:absolute; right:3px; width:16em; font-size:.9em;}
#sidebarOptions {padding-top:0.3em;}
#sidebarOptions a {margin:0em 0.2em; padding:0.2em 0.3em; display:block;}
#sidebarOptions input {margin:0.4em 0.5em;}
#sidebarOptions .sliderPanel {margin-left:1em; padding:0.5em; font-size:.85em;}
#sidebarOptions .sliderPanel a {font-weight:bold; display:inline; padding:0;}
#sidebarOptions .sliderPanel input {margin:0 0 .3em 0;}
#sidebarTabs .tabContents {width:15em; overflow:hidden;}

.wizard {padding:0.1em 1em 0em 2em;}
.wizard h1 {font-size:2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizard h2 {font-size:1.2em; font-weight:bold; background:none; padding:0em 0em 0em 0em; margin:0.4em 0em 0.2em 0em;}
.wizardStep {padding:1em 1em 1em 1em;}
.wizard .button {margin:0.5em 0em 0em 0em; font-size:1.2em;}
.wizardFooter {padding:0.8em 0.4em 0.8em 0em;}
.wizardFooter .status {padding:0em 0.4em 0em 0.4em; margin-left:1em;}
.wizard .button {padding:0.1em 0.2em 0.1em 0.2em;}

#messageArea {position:fixed; top:2em; right:0em; margin:0.5em; padding:0.5em; z-index:2000; _position:absolute;}
.messageToolbar {display:block; text-align:right; padding:0.2em 0.2em 0.2em 0.2em;}
#messageArea a {text-decoration:underline;}

.tiddlerPopupButton {padding:0.2em 0.2em 0.2em 0.2em;}
.popupTiddler {position: absolute; z-index:300; padding:1em 1em 1em 1em; margin:0;}

.popup {position:absolute; z-index:300; font-size:.9em; padding:0; list-style:none; margin:0;}
.popup .popupMessage {padding:0.4em;}
.popup hr {display:block; height:1px; width:auto; padding:0; margin:0.2em 0em;}
.popup li.disabled {padding:0.4em;}
.popup li a {display:block; padding:0.4em; font-weight:normal; cursor:pointer;}
.listBreak {font-size:1px; line-height:1px;}
.listBreak div {margin:2px 0;}

.tabset {padding:1em 0em 0em 0.5em;}
.tab {margin:0em 0em 0em 0.25em; padding:2px;}
.tabContents {padding:0.5em;}
.tabContents ul, .tabContents ol {margin:0; padding:0;}
.txtMainTab .tabContents li {list-style:none;}
.tabContents li.listLink { margin-left:.75em;}

#contentWrapper {display:block;}
#splashScreen {display:none;}

#displayArea {margin:1em 17em 0em 14em;}

.toolbar {text-align:right; font-size:.9em;}

.tiddler {padding:1em 1em 0em 1em;}

.missing .viewer,.missing .title {font-style:italic;}

.title {font-size:1.6em; font-weight:bold;}

.missing .subtitle {display:none;}
.subtitle {font-size:1.1em;}

.tiddler .button {padding:0.2em 0.4em;}

.tagging {margin:0.5em 0.5em 0.5em 0; float:left; display:none;}
.isTag .tagging {display:block;}
.tagged {margin:0.5em; float:right;}
.tagging, .tagged {font-size:0.9em; padding:0.25em;}
.tagging ul, .tagged ul {list-style:none; margin:0.25em; padding:0;}
.tagClear {clear:both;}

.footer {font-size:.9em;}
.footer li {display:inline;}

.annotation {padding:0.5em; margin:0.5em;}

* html .viewer pre {width:99%; padding:0 0 1em 0;}
.viewer {line-height:1.4em; padding-top:0.5em;}
.viewer .button {margin:0em 0.25em; padding:0em 0.25em;}
.viewer blockquote {line-height:1.5em; padding-left:0.8em;margin-left:2.5em;}
.viewer ul, .viewer ol {margin-left:0.5em; padding-left:1.5em;}

.viewer table, table.twtable {border-collapse:collapse; margin:0.8em 1.0em;}
.viewer th, .viewer td, .viewer tr,.viewer caption,.twtable th, .twtable td, .twtable tr,.twtable caption {padding:3px;}
table.listView {font-size:0.85em; margin:0.8em 1.0em;}
table.listView th, table.listView td, table.listView tr {padding:0px 3px 0px 3px;}

.viewer pre {padding:0.5em; margin-left:0.5em; font-size:1.2em; line-height:1.4em; overflow:auto;}
.viewer code {font-size:1.2em; line-height:1.4em;}

.editor {font-size:1.1em;}
.editor input, .editor textarea {display:block; width:100%; font:inherit;}
.editorFooter {padding:0.25em 0em; font-size:.9em;}
.editorFooter .button {padding-top:0px; padding-bottom:0px;}

.fieldsetFix {border:0; padding:0; margin:1px 0px 1px 0px;}

.sparkline {line-height:1em;}
.sparktick {outline:0;}

.zoomer {font-size:1.1em; position:absolute; overflow:hidden;}
.zoomer div {padding:1em;}

* html #backstage {width:99%;}
* html #backstageArea {width:99%;}
#backstageArea {display:none; position:relative; overflow: hidden; z-index:150; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageToolbar {position:relative;}
#backstageArea a {font-weight:bold; margin-left:0.5em; padding:0.3em 0.5em 0.3em 0.5em;}
#backstageButton {display:none; position:absolute; z-index:175; top:0em; right:0em;}
#backstageButton a {padding:0.1em 0.4em 0.1em 0.4em; margin:0.1em 0.1em 0.1em 0.1em;}
#backstage {position:relative; width:100%; z-index:50;}
#backstagePanel {display:none; z-index:100; position:absolute; width:90%; margin:0em 3em 0em 3em; padding:1em 1em 1em 1em;}
.backstagePanelFooter {padding-top:0.2em; float:right;}
.backstagePanelFooter a {padding:0.2em 0.4em 0.2em 0.4em;}
#backstageCloak {display:none; z-index:20; position:absolute; width:100%; height:100px;}

.whenBackstage {display:none;}
.backstageVisible .whenBackstage {display:block;}
/*}}}*/
/***
StyleSheet for use when a translation requires any css style changes.
This StyleSheet can be used directly by languages such as Chinese, Japanese and Korean which need larger font sizes.
***/
/*{{{*/
body {font-size:0.8em;}
#sidebarOptions {font-size:1.05em;}
#sidebarOptions a {font-style:normal;}
#sidebarOptions .sliderPanel {font-size:0.95em;}
.subtitle {font-size:0.8em;}
.viewer table.listView {font-size:0.95em;}
/*}}}*/
/*{{{*/
@media print {
#mainMenu, #sidebar, #messageArea, .toolbar, #backstageButton, #backstageArea {display: none ! important;}
#displayArea {margin: 1em 1em 0em 1em;}
/* Fixes a feature in Firefox 1.5.0.2 where print preview displays the noscript content */
noscript {display:none;}
}
/*}}}*/
<!--{{{-->
<div class='header' macro='gradient vert [[ColorPalette::PrimaryLight]] [[ColorPalette::PrimaryMid]]'>
<div class='headerShadow'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
<div class='headerForeground'>
<span class='siteTitle' refresh='content' tiddler='SiteTitle'></span>&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>
</div>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>
<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::ViewToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date'></span> (<span macro='message views.wikified.createdPrompt'></span> <span macro='view created date'></span>)</div>
<div class='tagging' macro='tagging'></div>
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
<!--}}}-->
<!--{{{-->
<div class='toolbar' macro='toolbar [[ToolbarCommands::EditToolbar]]'></div>
<div class='title' macro='view title'></div>
<div class='editor' macro='edit title'></div>
<div macro='annotations'></div>
<div class='editor' macro='edit text'></div>
<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>
<!--}}}-->
To get started with this blank TiddlyWiki, you'll need to modify the following tiddlers:
* SiteTitle & SiteSubtitle: The title and subtitle of the site, as shown above (after saving, they will also appear in the browser title bar)
* MainMenu: The menu (usually on the left)
* DefaultTiddlers: Contains the names of the tiddlers that you want to appear when the TiddlyWiki is opened
You'll also need to enter your username for signing your edits: <<option txtUserName>>
These InterfaceOptions for customising TiddlyWiki are saved in your browser

Your username for signing your edits. Write it as a WikiWord (eg JoeBloggs)

<<option txtUserName>>
<<option chkSaveBackups>> SaveBackups
<<option chkAutoSave>> AutoSave
<<option chkRegExpSearch>> RegExpSearch
<<option chkCaseSensitiveSearch>> CaseSensitiveSearch
<<option chkAnimate>> EnableAnimations

----
Also see AdvancedOptions
<<importTiddlers>>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-04-02","description":"The AMP Auto Skip command now utilizes Preroll if the preroll value is greater than or equal to the K2’s specified preroll setting (10 frames). The Auto Skip Option 1 was removed from the spec because it was not supported. A new option was added to this command, option 4. You can now schedule an Auto Skip against timecode, just as you would do with Play, Stop, or Record. The AMP Specification was update to reflect this new command option.","buildsWithFix":"K2 3.2.58.858","assocCRs":"90348","checkinTimeBranch":"4/2/2008 10:53 AM / K2_maint_4\r\n4/3/2008 2:43 PM / Main \r\n4/2/2008 2:52 PM / K2_3.x"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"buildsWithFix":"K2 3.2.58.858","date":"2008-04-02","description":"Four AMP Channelless commands were not working correctly under certain conditions. The Set Mark In, Set Mark Out, ID Start Time, and ID Duration Request commands all were not performing correctly because a channelless connection does not have a video format. These commands were changed to extract the video format from the clips they are performing operations on. ","checkinTimeBranch":"4/2/2008 10:53 AM / K2_maint_4\r\n4/3/2008 2:43 PM / Main \r\n4/2/2008 2:52 PM / K2_3.x","assocCRs":"89952"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-01-29","description":"Resolves issue with a checkin made on 1/4/08 in ParseClipNames(). If the same clip was already cued when a cue clip command came in (In Preset or Preview In Preset), some of the status bits were not getting set correctly.","assocCRs":"86634","checkinTimeBranch":"1/29/2008 6:25 PM - K2_maint_4\n2/1/2008 1:19 PM - Main\n","buildsWithFix":"K2 3.2.58.831"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-02-29","description":"Only update channel info for active context. AMP was being told that the current clip was in fact the clip that was cued on the preview context.","assocCRs":"88538","checkinTimeBranch":"2/29/2008 9:50 AM - K2_maint_4\n2/29/2008 9:52 AM - Main\n2/29/2008 9:51 AM - K2_3.x\n","buildsWithFix":"K2 3.2.58.839"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-04-02","description":"The AMP Device Type Request now sets bit 0 high if in PAL mode.","assocCRs":"83028","buildsWithFix":"K2 3.2.58.858","checkinTimeBranch":"4/2/2008 10:53 AM / K2_maint_4\r\n4/3/2008 2:43 PM / Main \r\n4/2/2008 2:52 PM / K2_3.x"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"description":"The Drop Frame status was being reported in PAL mode.","date":"2008-04-02","buildsWithFix":"K2 3.2.58.858","assocCRs":"90350","checkinTimeBranch":"4/2/2008 10:53 AM / K2_maint_4\r\n4/3/2008 2:43 PM / Main \r\n4/2/2008 2:52 PM / K2_3.x"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2007-12-13","description":"A change was checked into K2 code to fix an issue identified with the AMP Erase Segment command. The command would sometimes not clean up unused media because the new marks were not being flushed to the database quick enough. This was fixed by adding a call to manually flush the changes to the DB. ","checkinTimeBranch":"12/13/2007 4:16 PM - K2_maint_4\n12/13/2007 4:16 PM - Main\n12/13/2007 4:16 PM - k2_3.x","buildsWithFix":"K2 3.2.58.817"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-04-02","description":"This fix changes the behavior in AMP PreviewInPreset command if it is given an invalid clip name. Previously: stops currently playing (In Preset) clip and cues to Mark In. Now: currently playing clip continues playing, clip not found message logged, IDNotFound status bit is set.","buildsWithFix":"K2 3.2.58.858","assocCRs":"78507","checkinTimeBranch":"4/2/2008 10:09 AM / K2_maint_4\r\n4/2/2008 9:01 AM / Main\r\n4/2/2008 9:00 AM / K2_3.x"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-01-04","description":"A change was checked into K2 code to fix an issue identified with the In Preset commands. You can now cue clips with full paths from various different folders, all at once, on the same timeline. The ID Loaded Request now also returns the currently playing clip correctly, after multiple clips from different directories had been cued at once, on one timeline. ","assocCRs":"86634","checkinTimeBranch":"1/4/2008 3:53 PM - K2_maint_4\n2/1/2008 1:19 PM - Main\n","buildsWithFix":"K2 3.2.58.820"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"description":"Properly handle clip object lists when Preview In Preset command is issued with no clip loaded on the In Preset timeline.","date":"2008-02-29","assocCRs":"88557","checkinTimeBranch":"2/29/2008 9:57 AM - K2_maint_4\n2/29/2008 9:58 AM - Main\n3/3/2008 12:12:21 PM - K2_3.x\n","buildsWithFix":"K2 3.2.58.839"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-04-02","description":"The AMP Play command was not using preroll if the player was stopped at the end of the In Preset clip. It now uses the preroll setting if the preroll is greater than or equal to the specified preroll setting (10 frames on K2).","buildsWithFix":"K2 3.2.58.858","assocCRs":"90352","checkinTimeBranch":"4/2/2008 10:53 AM / K2_maint_4\r\n4/3/2008 2:43 PM / Main \r\n4/2/2008 2:52 PM / K2_3.x"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-04-02","buildsWithFix":"K2 3.2.58.858","assocCRs":"90347","checkinTimeBranch":"4/2/2008 10:53 AM / K2_maint_4\r\n4/3/2008 2:43 PM / Main \r\n4/2/2008 2:52 PM / K2_3.x","description":"The AMP ability to retrieve the clip lists from the Preview Preset and Preset commands using option 2 of the commands was broken. It would not pass the clip lists from the Preview Timeline to the Preset timeline when the clips in the Preview Timeline transitioned to playing (when they were activated)."}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-04-02","buildsWithFix":"K2 3.2.58.858","assocCRs":"90351","checkinTimeBranch":"4/2/2008 10:53 AM / K2_maint_4\r\n4/3/2008 2:43 PM / Main \r\n4/2/2008 2:52 PM / K2_3.x","description":"The AMP Preview Out Preset option 2, clip list retrieval, was not subtracting the Mark In from the Mark Out when returning the Out Preset times when Timer timecode mode was set."}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-04-02","buildsWithFix":"K2 3.2.58.858","assocCRs":"90349","checkinTimeBranch":"4/2/2008 10:53 AM / K2_maint_4\r\n4/3/2008 2:43 PM / Main\r\n4/2/2008 2:52 PM / K2_3.x","description":"The AMP Preview Out Preset status bit was being set low in the In Preset command Option 2, command was received. This is simply the retrieval of the clip list and should not have modified any status bits."}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"description":"It was found that the Preview In and Preview Out status bits were being set high prematurely. This fix addresses that and the bits are now set only when the decoder finishes cueing the clip.","date":"2008-04-02","buildsWithFix":"K2 3.2.58.858","assocCRs":"89866","checkinTimeBranch":"4/2/2008 10:53 AM / K2_maint_4\r\n4/3/2008 2:43 PM / Main \r\n4/2/2008 2:52 PM / K2_3.x\r\n"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2007-12-12","description":"A change was checked into K2 code to fix an issue identified with the recently added AMP Replace Edit feature. When connected to a channel-less AMP connection, the Replace Edit command was not properly acquiring clip properties for the virtual clip ‘<BLACK>’. ","assocCRs":"86355","checkinTimeBranch":"12/12/2007 10:44 AM - K2_maint_4\n12/12/2007 10:44 AM - Main\n12/12/2007 10:44 AM - k2_3.x","buildsWithFix":"K2 3.2.58.814"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-02-29","description":"Changing shuttle function to CommitChange() as type NONE. Lack of the choice 'none' always forces a recue of the preview context when attempting to change the shuttle speed on the preview context. Shuttles near the end of a context can mean that the next context will fail to cue in time.","assocCRs":"85999","checkinTimeBranch":"2/29/2008 9:57 AM - K2_maint_4\n2/29/2008 9:58 AM - Main\n3/3/2008 12:12:21 PM - K2_3.x\n","buildsWithFix":"K2 3.2.58.839"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2007-12-12","description":"Changes were checked into K2 code to fix some issues identified with the recently added AMP End Mode feature. When in Variable Play mode or Fast Forward mode, if an End Mode was set, the playback would not stop at the end of the play timeline like it should have. Also, when playing at a speed other than 1.0 (play speed), if the End Mode was switched back to Default, the play speed for the preview timeline was always being set to 1.0. It is now set to the speed that the channel is currently playing at. Another fix found is when the Jog command was issued; it also could be applied to the preview context. ","checkinTimeBranch":"12/12/2007 10:44 AM - K2_maint_4\n12/12/2007 10:44 AM - Main, k2_3.x\n12/12/2007 10:44 AM - k2_3.x\n","assocCRs":"86477","buildsWithFix":"K2 3.2.58.814"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-04-02","description":"Time of Day timecode mode support was added. It is now supported by the Timecode Mode Preset (41.36) command. Time of Day is set with Send Data equal to 0x03. A time of day status bit was also implemented to reflect the new timecode mode. The status bit is Byte D, bit 0. The AMP specification was also updated to reflect this.","buildsWithFix":"K2 3.2.58.858","checkinTimeBranch":"4/2/2008 10:53 AM / K2_maint_4\r\n4/3/2008 2:43 PM / Main\r\n4/2/2008 2:52 PM / K2_3.x","assocCRs":"90346"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-01-24","description":"Issue fixed regarding caching clips in a directory with a UTF-8 directory name.","assocCRs":"86944","checkinTimeBranch":"1/24/2008 11:40 AM - K2_maint_4\n2/1/2008 1:19 PM - Main\n","buildsWithFix":"K2 3.2.58.831"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-02-29","description":"User Data is deleted upon trimming a clip. This is ok if the data is associated with a field number in the clip that has been trimmed, but user data that is associated with the entire clip as a whole was also being deleted.","assocCRs":"87856","checkinTimeBranch":"2/29/2008 9:44 AM / K2_maint_4\n2/29/2008 9:52 AM / Main\n2/29/2008 9:46 AM / K2_3.x\n","buildsWithFix":"K2 3.2.58.839"}</data>
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2007-12-06","description":"A change was checked into K2 code to fix a cueing issue related to clips that were recorded past the midnight boundary. Passing the midnight boundary means a clip’s timecode will increase to 23:59:59:29 (NTSC Example) then be followed by the timecode 00:00:00:00 in the next frame. If you tried to cue a clip to a timecode greater than or equal to 00:00:00:00 inside that clip, the cue operation would fail. This issue only occurred when the timecode mode was set to Timer Mode. Cueing to absolute timecode did not experience this issue. The issue currently still exists in the BVW protocol for both Timer and Absolute timecode modes, and will be addressed in another future build.","buildsWithFix":"K2 3.2.58.808","checkinTimeBranch":"12/6/2007 4:15 PM - K2_maint_4\n12/6/2007 4:15 PM - main, k2_3.x\n12/6/2007 4:15 PM - k2_3.x","assocCRs":"85945"}</data>
!K2 ~AppServer Developer's Guide © Grass Valley 2007
The K2 ~AppServer Developer's Guide was written using Jeremy Ruston's excellent [[TiddlyWiki|http://www.tiddlywiki.com/]] (version: <<version>>). ~TiddlyWiki is used under a [[BSD open source license|http://www.tiddlywiki.com/#OpenSourceLicense]].

This guide also uses plugins from [[abego Software GmbH|http://tiddlywiki.abego-software.de/#Plugins]]. The plugins are used under a [[BSD open source license|http://tiddlywiki.abego-software.de/#%5B%5BBSD%20open%20source%20license%5D%5D%20About]].

The guide also references [[Oleg Shilo's CS-Script|http://www.members.optusnet.com.au/~olegshilo/index.html]] program.

''Copyright''
Copyright © 2007 Grass Valley, Inc. All rights reserved. Printed in the United States of America. Portions of software © 2000 – 2007, Microsoft Corporation. All rights reserved. This document may not be copied in whole or in part, or otherwise reproduced except as specifically permitted under U.S. copyright law, without the prior written consent of Grass Valley, Inc., P.O. Box 59900, Nevada City, California 95959-7900. This product may be covered by one or more U.S. and foreign patents.

''Disclaimer''
Product options and specifications subject to change without notice. The information in this manual is furnished for informational use only, is subject to change without notice, and should not be construed as a commitment by Grass Valley, Inc. Grass Valley, Inc. assumes no responsibility or liability for any errors or inaccuracies that may appear in this publication.

''U.S. Government Restricted Rights Legend''
Use, duplication, or disclosure by the United States Government is subject to restrictions as set forth in subparagraph (c)(1)(ii) of the Rights in Technical Data and Computer Software clause at DFARS 252.277-7013 or in subparagraph c(1) and (2) of the Commercial Computer Software Restricted Rights clause at FAR 52.227-19, as applicable. Manufacturer is Grass Valley, Inc., P.O. Box 59900, Nevada City, California 95959-7900 U.S.A.

''Trademarks and Logos''
Grass Valley, K2, Aurora, Turbo, ~M-Series, Profile, Profile XP, ~NewsBrowse, ~NewsEdit, ~NewsQ, ~NewsShare, ~NewsQ Pro, and Media Manager are either registered trademarks or trademarks of Grass Valley, Inc. in the United States and/or other countries. Grass Valley, Inc. products are covered by U.S. and foreign patents, issued and pending. Additional information regarding Grass Valley, Inc. trademarks and other proprietary rights may be found at www.thomsongrassvalley.com. 
Other trademarks and logos used in this document are either registered trademarks or trademarks of the manufacturers or vendors of the associated products, such as Microsoft® Windows® operating system, Windows Media® player, Internet Explorer® internet browser, and SQL Server™. ~QuickTime and the ~QuickTime logo are trademarks or registered trademarks of Apple Computer, Inc., used under license therefrom.
''Suites and Security:''
When a client requests an ~AppServer, it passes in a unique suite name and a user name. The concept is similar to someone using an editing suite. While that person is in the suite, they have access to all of their own resources. Similarly, each ~AppServer for a user and suite has its own resources. This is also what tells ~AppService whether-or-not to create a new ~AppServer. If two client applications request an ~AppServer with the same suite name and user name, then both applications will get back the same ~AppServer object. See [[Suspending and Closing AppServers]] for an explanation of when an existing ~AppServer would be returned.

''Channel stealing:''
Channels on the K2 are shared resources. If a client is using a channel and another client requests to use that same channel, the channel will be stolen away from the first client and given to the second client. If it is important to prohibit this, the applicatoin should first check to see if the channel is owned by another application first. See the [[Examples]] section for channel owner example code.
This information is taken fom the “K2 Media Client User Guide”, Chapter 7, Working with assets – Renaming an asset:
 
Asset names can contain up to 32 characters, including spaces. Spaces count as two characters.

The following characters are not allowed: 

\ (backward slash)
/ (forward slash)
&#58; (colon)
&#42; (asterisk)
? (question mark)
&lt; (less than)
&gt; (greater than)
&#166; (pipe) 
" (double quote)
Below are the asset properties you can query using ~IMediaMgr.~GetProperty function. See [[Get asset properties]] for example code showing how to get one or more asset properties.

|!Property|!Unmanaged Type|!Managed Type|!Description|!Notes|
|~AspectRatio|BSTR|System.String|Returns a string representing the asset's aspect ration. Return values include:{{{"4:3", "16:9"}}}||
|~AspectRatioConversion|BSTR|System.String|Returns a string indicating the aspect ratio conversion type: Return values include: {{{"Default", "Bars", "Halfbars", "Crop", "Stretch", "Mixed"}}}||
|~AssetType|BSTR|System.String|String indicates the asset type. Values include {{{"Clip", "Program", "List"}}}||
|Attributes1|long|int|Returns an int that is a bitfield of movie properties.||
|Attributes2|long|System.Int32|Returns an int that is a bitfield of more movie properties.||
|~AudioFormat|BSTR|System.String|Returns a string indicating the audio format. Return values include:<br/>{{{"AC3", "Dolby E", "Data", "PCM", "Mixed"}}}||
|Bin|BSTR|System.String|Returns a string name of the bin that contains the asset.|e|
|~BinId|BSTR|System.String|Returns a string that is the bin's UUID. This is the same as the ~GroupId property.||
|~BPix|long|System.Int32|Returns an int indicating the number of B pictures per I and P pictures in a GOP. This is for LGOP MPEG assets only.||
|Construction|BSTR|System.String|Indicates the asset’s construction status. Values include:<br/>{{{"CodecConstruction", "CopyConstruction", "StreamConstruction", "RestoreConstruction", and "DoneConstruction".}}}||
|~ContinuousRecord|~VARIANT_BOOL|System.Boolean|Returns a Boolean indicating whether the asset is a continuous recording.||
|Created|DATE|System.~DateTime|Returns a DateTime when the asset was created.||
|~DropFrame|~VARIANT_BOOL|System.Boolean|Returns a Boolean indicating whether the asset's timecode media is drop-frame.||
|~EventCount|long|System.Int32|Returns an int value indicating the number of events in the List. (valid if the asset is a List).|e|
|~FieldsPerFrame|long|System.Int32|Returns a int indicating the number of fields per frame.||
|~FileLength|long|System.Int32|Difference between maxMarkOut and minMarkIn properties.|e|
|~FileLengthStr|BSTR|System.String|Timecode string representation of the difference between maxMarkOut and minMarkIn properties.|e|
|~FirstTimecodeMask|long|System.Int32|Cached timecode value from the start of the first timecode media file. Timecode is represented as a 32 bit mask.||
|~GroupId|BSTR|System.String|Returns a string that is the group's UUID. This is the same as the ~BinId property.||
|~HiDef|~VARIANT_BOOL|System.Boolean|Returns a Boolean indicating if the asset is an MPEG high definition format.||
|~IPix|long|System.Int32|Returns an int indicating the number of I pictures per GOP. This is for LGOP MPEG assets only.||
|~IsInUse|~VARIANT_BOOL|System.Boolean|~VARIANT_TRUE if the movie is locked, has another writable instance open, is under construction, or is loaded on the system processing the property query.||
|~LastTimecodeMask|long|System.Int32|Cached timecode value from the end of the last timecode media file. Timecode is represented as a 32 bit mask.||
|~LengthStr|BSTR|System.String|String representing the ~MaxLength value converted to timecode. A ‘;’ character before the frame value indicates dropframe.||
|Locked|~VARIANT_BOOL|System.Boolean|Returns a Boolean indicating if the asset is locked.||
|~MarkIn|long|System.Int32|Asset mark in value. Does not necessarily reflect media marks.||
|~MarkInStr|BSTR|System.String|String representation of the timecode from the start of the first timecode media file on the first timecode track. A ‘;’ or ‘,’ character before the frame value indicates dropframe.||
|~MarkOut|long|System.Int32|Asset mark out value. Does not necessarily reflect media marks.||
|~MarkOutStr|BSTR|System.String|String representation of the timecode from the end of the last timecode media file on the first timecode track. Note that this value is one frame greater than the last playable timecode frame. A ‘;’ or ‘,’ character before the frame value indicates dropframe.||
|~MaxLength|long|System.Int32|Number of fields in longest track (the sum of all media durations).||
|~MaxMarkOut|long|System.Int32|This is the largest value the mark out can be set to and still show media on all tracks.|e|
|~MaxMarkOutStr|BSTR|System.String|This is a string representation of the smallest timecode value the mark in can be set to and still show media on all tracks. The timecode value is mathematically calculated using the firstTimecode property.|e|
|~MinLength|long|System.Int32|Returns a int value that is the number of fields in the shortest track.||
|~MinMarkIn|long|System.Int32|This is the smallest value the mark in can be set to and still show media on all tracks.|e|
|~MinMarkInStr|BSTR|System.String|This is a string representation of the smallest timecode value the mark in can be set to and still show media on all tracks. The timecode value is mathematically calculated using the firstTimecode property.|e|
|Modified|DATE|System.~DateTime|Date last modified.||
|~MovieId|BSTR|System.String|32 character unique identifier (UUID)||
|~MovieSize|long|System.~UInt32|The sum of media file sizes and user data in megabytes (MB). If the movie only references a portion of a file, then only a percent of the file size value is used.|e, x|
|Name|BSTR|System.String|Asset name||
|~NumA|long|System.Int32|	Returns an int value that is number of audio tracks.||
|~NumAncData|long|System.Int32|Returns an int value that is number of ancillary data tracks.||
|~NumT|long|System.Int32|Returns an int value that is number of timecode tracks.||
|~NumV|long|System.Int32|Returns an int value that is number of video tracks.||
|~PPix|long|System.Int32|Returns an int indicating the number of P pictures per I pictures in a GOP. This is for LGOP MPEG assets only.||
|~ReadOnly|~VARIANT_BOOL|System.Boolean|Returns a Boolean indicating if the asset is locked or is a read-only instance (which can happen if some other application has the asset already open for writing). Applications can only lock and unlock assets. ~ReadOnly is merely reflects the current state of the asset.||
|~ReferenceStandard|long|System.Int32|Returns an int value indicating the reference standard.<br/>All 59.95/29.97Hz formats report NTSC = 0:<br/>{{{Format525_29_97Hz_2To1 = 0, Format525_29_97Hz_1To1 = 0, Format525_59_94Hz_1To1 = 0, Format720_59_94Hz_1To1 = 0, Format720_25Hz_1To1, Format720_29_97Hz_1To1 = 0, Format1080_29_97Hz_2To1 = 0, Format1080_29_97Hz_1To1 = 0, Format1440x1080_29_97Hz_2To1 = 0, FormatMixed_29_97HzRef = 0}}}<br /><br/>All 50/25 Hz formats report PAL = 1:<br/>{{{Format625_25Hz_2To1 = 1, Format625_25Hz_1To1 = 1, Format625_50Hz_1To1 = 1, Format720_50Hz_1To1 = 1, Format720_25Hz_1To1 = 1, Format1080_25Hz_2To1 = 1, Format1080_25Hz_1To1 = 1, Format1440x1080_25Hz_2To1 = 1, FormatMixed_25HzRef = 1}}}||
|~ReferenceStandardStr|BSTR|System.String|Returns a string indicating the reference standard. Return values include:<br/>{{{"Format525_60Hz_2To1", "Format625_50Hz_2To1", "Format720_59_94Hz_1To1", "Format720_60Hz_1To1", "Format1080_23_98Hz_1To1", "Format1080_24Hz_1To1", "Format1080_25Hz_1To1", "Format1080_29_97Hz_1To1", "Format1080_30Hz_1To1", "Format1080_25Hz_2To1", "Format1080_29_97Hz_2To1", "Format1080_30Hz_2To1", "Format1035_30Hz_2To1", "Format1035_29_97Hz_2To1", "Format720_50Hz_1To1", "Format525_59_94Hz_1To1", "Format525_60Hz_1To1", "Format525_29_97Hz_1To1", "Format525_30Hz_1To1", "Format625_50Hz_1To1", "Format625_25Hz_1To1", "Format720_29_97Hz_1To1", "Format720_30Hz_1To1", "Format720_25Hz_1To1", "Format1440x1080_29_97Hz_2To1", "Format1440x1080_25Hz_2To1", "FormatXga_59_94Hz_1To1", "FormatXga_50Hz_1To1", "FormatWxga_59_94Hz_1To1", "FormatWxga_50Hz_1To1", "FormatWsxga_59_94Hz_1To1", "FormatWsxga_50Hz_1To1", "FormatMixed_29_97HzRef", "FormatMixed_25HzRef", "Unknown Video Format", "Invalid Video Format"}}}||
|Repeat|~VARIANT_BOOL|System.Boolean|Returns a Boolean indicating if the asset is a List and it has the "repeat" property set.||
|~SectionCount|long|System.Int32|Number of sections in a List asset.|e|
|Size|long|System.Int32|Returns an int value indicating the asset's data size.||
|~StartTimecode|long|System.Int32|Arbitrary starting timecode for a List asset. Timecode is represented as a 32 bit mask.|e|
|~StartTimecodeStr|BSTR|System.String|Returns an int value that is the starting timecode of a List asset. Timecode is represented as a 32-bit mask.|e|
|Summary|BSTR|System.String|Returns a string that is the summary of the asset structure. This string is used by utility programs like ~ViewMovie. (Note: summary written in English.) |e, x|
|~ThumbnailImage|BYTE SAFEARRAY|System.Object[]|Returns an array of bytes containing the asset's thumbnail image. You should call ~MediaMgr.~GetThumbnail() instead.|e|
|~ThumbnailMark|long|System.Int32|Returns an int value that is the thumbnail's mark value. This value is relative to the asset's mark in value. For example, if the thumbnail is taken 32 fields into a clip whose mark in value is 100, then the thumbnail mark value would be 132.|e|
|~UnderConstruction|~VARIANT_BOOL~|System.Boolean|~VARIANT_TRUE if the asset is under any type of construction.||
|URI|BSTR|System.String|Asset identifier in URI format. The URI string takes the following form: {{{edl/cmf//<machine>/<volume>/<bin>/<name>}}}|e|
|Version|long|System.Int32|Version of this property list. Currently set to 1.||
|~VideoCompressionType|BSTR|System.String|Returns a string indicating the video compression type. Return values include:<br/>{{{"Mixed Mpeg HD/SD", "Mixed MpegSD/DV", "Mixed DV HD/SD", "Mixed DV", "MpegHiDef", "DVCAM", "DVCPRO25", "DVCPRO50", "DVCPROHD", "Jpeg", "MpegD10", "MpegStdDef", "Unknown"}}}||
|~VideoFormat|long|System.Int32|Asset video format. Returns an int value that is one of these enumerate values:<br/>{{{Format525_29_97Hz_2To1 = 0, Format525_29_98Hz_2To1 = 0, Format525_60Hz_2To1 = 0, Format625_25Hz_2To1 = 1, Format625_50Hz_2To1 = 1, Format720_59_94Hz_1To1 = 2, Format720_60Hz_1To1 = 3, Format1080_23_98Hz_1To1 = 4, Format1080_24Hz_1To1 = 5, Format1080_25Hz_1To1 = 6, Format1080_29_97Hz_1To1 = 7, Format1080_30Hz_1To1 = 8, Format1080_25Hz_2To1 = 9, Format1080_29_97Hz_2To1 = 10, Format1080_30Hz_2To1 = 11, Format1035_30Hz_2To1 = 12, Format1035_29_97Hz_2To1 = 13, Format720_50Hz_1To1 = 14, Format525_59_94Hz_1To1 = 15, Format525_60Hz_1To1 = 16, Format525_29_97Hz_1To1 = 17, Format525_30Hz_1To1 = 18, Format625_50Hz_1To1 = 19, Format625_25Hz_1To1 = 20, Format720_29_97Hz_1To1 = 21, Format720_30Hz_1To1 = 22, Format720_25Hz_1To1 = 23, Format1440x1080_29_97Hz_2To1 = 24, Format1440x1080_30Hz_2To1 = 25, Format1440x1080_25Hz_2To1 = 26, FormatXga_59_94Hz_1To1 = 27, FormatXga_50Hz_1To1 = 28, FormatWxga_59_94Hz_1To1 = 29, FormatWxga_50Hz_1To1 = 30, FormatWsxga_59_94Hz_1To1, FormatWsxga_50Hz_1To1, FormatMixed_29_97HzRef = 0xf0, FormatMixed_25HzRef = 0xf1, VideoFormat_NA = 0xfe, VideoFormatInvalid = 0xff}}}||
|~VideoFormatStr|BSTR|System.String|Returns a string that is the asset's video format. Values include:<br/>{{{"Format525_60Hz_2To1", "Format625_50Hz_2To1", "Format720_59_94Hz_1To1", "Format720_60Hz_1To1", "Format1080_23_98Hz_1To1", "Format1080_24Hz_1To1", "Format1080_25Hz_1To1", "Format1080_29_97Hz_1To1", "Format1080_30Hz_1To1", "Format1080_25Hz_2To1", "Format1080_29_97Hz_2To1", "Format1080_30Hz_2To1", "Format1035_30Hz_2To1", "Format1035_29_97Hz_2To1", "Format720_50Hz_1To1", "Format525_59_94Hz_1To1", "Format525_60Hz_1To1", "Format525_29_97Hz_1To1", "Format525_30Hz_1To1", "Format625_50Hz_1To1", "Format625_25Hz_1To1", "Format720_29_97Hz_1To1", "Format720_30Hz_1To1", "Format720_25Hz_1To1", "Format1440x1080_29_97Hz_2To1", "Format1440x1080_30Hz_2To1", "Format1440x1080_25Hz_2To1", "FormatXga_59_94Hz_1To1", "FormatXga_50Hz_1To1", "FormatWxga_59_94Hz_1To1", "FormatWxga_50Hz_1To1", "FormatWsxga_59_94Hz_1To1", "FormatWsxga_50Hz_1To1", "FormatMixed_29_97HzRef", "FormatMixed_25HzRef",}}}<br/>{{{"Unknown Video Format", }}}<br/>{{{"Invalid Video Format"}}}||
|volume|BSTR|System.String|Returns a string indicating the volume name (e.g. "V:") associated with the asset's bin.|e|
|XML|BSTR|System.String|Returns a string that is an XML representation of the movie. |e, x|

Notes:
e – Best performance with one of the Editor interfaces (~ISimpleEditor, ~IEventEditor). Properties with this note can still be obtained by ~IMediaMgr::~GetProperty, but extra database transactions have to be made that makes these properties more expensive.
x – Expensive. This properties should only be obtained occasionally and as a result of a user request, rather than on a timer.
To Get and Set user data for an asset use the ~MediaMgr functions show in the examples below.


{{{
[C#]
// get a MediaMgr object
using ( IMediaMgr mediaMgr = connection.AppServer.CreateMediaMgr(appName) )
{
 int cookie = -1;
 try
 {
 // define an asset that we want to get & set userdata
 string clip = "edl/cmf//local/V:/default/Clip";
 
 // add userdate - string, integer, float, boolean, date
 mediaMgr.AddMetadata (clip, "SomeName", "SomeValue");
 mediaMgr.AddMetadata (clip, "SomeInteger", 5);
 mediaMgr.AddMetadata (clip, "SomeFloat", (float) 3.14);
 mediaMgr.AddMetadata (clip, "SomeBoolean", true);
 mediaMgr.AddMetadata (clip, "SomeDate", DateTime.Now);
 
 // select the user data for a clip
 int count = 0;
 cookie = mediaMgr.SelectMetadata (clip, ref count);
 Console.WriteLine("count = " + count);
 
 if ( count > 0 )
 {
 // get all user data tags
 string tags;
 mediaMgr.GetAllMetadataTags(out count, out tags);
 
 // print tags - see sample GetAllMetadataTags below class
 Console.WriteLine(tags);
 Console.WriteLine("-----------");

 // using GetXmlResults get all userdata for a clip in XML format
 int maxCount = 512; // loop thru all results in blocks of 512 lines
 // keep in 256 - 512 range for best performance

 int i = 0; // used to indicate where to start
 string xmlData = "";
 count = maxCount;

 // loop thru all results until done
 while (count == maxCount)
 {
 // get a block of user data and print it out
 mediaMgr.GetXmlResults(cookie, i * maxCount, maxCount, 
 ref count, out xmlData);
 Console.WriteLine(xmlData);

 // see sample GetXmlResults below class
 
 i++;
 }
 }
 
 // remove metadata - get ExtensionID from XML data from GetXmlResults call. 
 // hardcoded it here for simple example.
 string extensionID = "443c567a96394a679ccee6ecf17648b8";
 mediaMgr.RemoveMetadata (clip, extensionID);
 }
 catch (COMException ce)
 {
 Console.WriteLine("ERROR: caught COM Exception 0x{0:X}.", ce.ErrorCode);
 Console.WriteLine("Probably couldn't find metadata with that ExtensionID");
 }
 finally
 {
 if (cookie > -1)
 mediaMgr.CloseResults(cookie); 
 }
}
}}}
{{{
[C++]
// get a MediaMgr object
IMediaMgrPtr spMediaMgr;
hr = spAppServer->CreateMediaMgr(m_bstrAppName, &spMediaMgr);

// define an asset that we want to get & set userdata
_bstr_t bstrClip("edl/cmf//local/V:/default/Clip");

// add userdata - string, integer, float, boolean
spMediaMgr->AddMetadata(bstrClip, _bstr_t("SomeName"), _variant_t("SomeValue"));
spMediaMgr->AddMetadata(bstrClip, _bstr_t("SomeInteger"), _variant_t(5));
spMediaMgr->AddMetadata(bstrClip, _bstr_t("SomeFloat"), _variant_t((float) 3.14));
spMediaMgr->AddMetadata(bstrClip, _bstr_t("SomeBoolean"), _variant_t(TRUE));

// add userdata - date
COleDateTime oleDate(2008,3,17,12,15,0); 
_variant_t varDate;
varDate.date = oleDate;
varDate.vt = VT_DATE;
spMediaMgr->AddMetadata(bstrClip, _bstr_t("SomeDate"), varDate);

// select the user data for a clip
long count = 0;
long cookie;
spMediaMgr->SelectMetadata(bstrClip, &count, &cookie);

if ( count > 0 )
{
 // get all user data tags
 BSTR bstrTags;
 spMediaMgr->GetAllMetadataTags(&count, &bstrTags);
 CString sTags = bstrTags;

 printf("%s\n", sTags);
 printf("-----------\n");

 // using GetXmlResults get all userdata for a clip in XML format
 int maxCount = 512; // loop thru all results in blocks of 512 lines
 // keep in 256 - 512 range for performance

 int i = 0; // used to indicate where to start
 BSTR bstrXmlData;
 count = maxCount;

 // loop thru all results until done
 while (count == maxCount)
 {
 // get a block of user data and print it out
 spMediaMgr->GetXmlResults(cookie, i * maxCount, maxCount, 
 &count, &bstrXmlData);
 CString sXmlData = bstrXmlData;
 printf("%s\n", sXmlData); 

 i++;
 }

 // remove metadata - get ExtensionID from XML data from GetXmlResults call. 
 // hardcoded here for simple example.
 _bstr_t bstrExtensionID = _bstr_t("443c567a96394a679ccee6ecf17648b8");
 spMediaMgr->RemoveMetadata (bstrClip, bstrExtensionID);
}
}}}

The ~MediaMgr function ''~GetAllMetadataTags()'' returns XML data that looks like this (for the above examples). See the "ns:Data" section towards the bottom for the data:
{{{
<XML>
 <Header version="1"/>
 <Schema name="Schema1" xmlns="urn:schemas-microsoft-com:xml-data" xmlns:dt="urn:schemas-microsoft-com:datatypes">
 <AttributeType name="metadataName" dt:type="string"/>
 <AttributeType name="metadataType" dt:type="string"/>
 <ElementType name="MetadataTag">
 <attribute type="metadataName"/>
 <attribute type="metadataType"/>
 </ElementType>
 <ElementType name="Data">
 <element type="MetadataTag"/>
 </ElementType>
 </Schema>
 <ns:Data xmlns:ns="x-schema:#Schema1">
 <ns:MetadataTag metadataName="SomeBoolean" metadataType="boolean"/>
 <ns:MetadataTag metadataName="SomeDate" metadataType="dateTime"/>
 <ns:MetadataTag metadataName="SomeFloat" metadataType="r4"/>
 <ns:MetadataTag metadataName="SomeInteger" metadataType="i4"/>
 <ns:MetadataTag metadataName="SomeName" metadataType="string"/>
 </ns:Data>
</XML>
}}}

The ~MediaMgr functions ''~SelectMetadata()''and ''~GetXmlResults()'' returns XML data that looks like this (for the above examples). See the "ns:Data" section towards the bottom for the data:
{{{
<Select Query="" StartIndex="">
 <Header version="1" selectionNamespace="x-schema:#Schema1"/>
 <Schema name="Schema1" xmlns="urn:schemas-microsoft-com:xml-data" xmlns:dt="urn:schemas-microsoft-com:datatypes">
 <AttributeType name="ExtensionId" dt:type="string"/>
 <AttributeType name="MovieId" dt:type="string"/>
 <AttributeType name="LocationId" dt:type="string"/>
 <AttributeType name="Position" dt:type="i4"/>
 <AttributeType name="DataClass" dt:type="string"/>
 <AttributeType name="DataCreator" dt:type="string"/>
 <AttributeType name="DataName" dt:type="string"/>
 <AttributeType name="DataVersion" dt:type="string"/>
 <AttributeType name="DataLength" dt:type="i4"/>
 <AttributeType name="DataType" dt:type="enumeration" dt:values="bin.base64 string ui1 ui2 ui4 i1 i2 i4 r4 r8 boolean dateTime Unknown"/>
 <AttributeType name="Data_bin.base64" dt:type="bin.base64"/>
 <AttributeType name="Data_boolean" dt:type="boolean"/>
 <AttributeType name="Data_dateTime" dt:type="dateTime"/>
 <AttributeType name="Data_i1" dt:type="i1"/>
 <AttributeType name="Data_i2" dt:type="i2"/>
 <AttributeType name="Data_i4" dt:type="i4"/>
 <AttributeType name="Data_ui1" dt:type="ui1"/>
 <AttributeType name="Data_ui2" dt:type="ui2"/>
 <AttributeType name="Data_ui4" dt:type="ui4"/>
 <AttributeType name="Data_r4" dt:type="r4"/>
 <AttributeType name="Data_r8" dt:type="r8"/>
 <AttributeType name="Data_string" dt:type="string"/>
 <ElementType name="Extension">
 <attribute type="ExtensionId"/>
 <attribute type="MovieId"/>
 <attribute type="LocationId"/>
 <attribute type="Position"/>
 <attribute type="DataCreator"/>
 <attribute type="DataClass"/>
 <attribute type="DataName"/>
 <attribute type="DataVersion"/>
 <attribute type="DataType"/>
 <attribute type="DataLength"/>
 <attribute type="Data_bin.base64"/>
 <attribute type="Data_boolean"/>
 <attribute type="Data_dateTime"/>
 <attribute type="Data_i1"/>
 <attribute type="Data_i2"/>
 <attribute type="Data_i4"/>
 <attribute type="Data_ui1"/>
 <attribute type="Data_ui2"/>
 <attribute type="Data_ui4"/>
 <attribute type="Data_r4"/>
 <attribute type="Data_r8"/>
 <attribute type="Data_string"/>
 </ElementType>
 <ElementType name="Data">
 <element type="Extension"/>
 </ElementType>
 </Schema>
 <ns:Data xmlns:ns="x-schema:#Schema1">
 <ns:Extension ExtensionId="443c567a96394a679ccee6ecf17648b8" MovieId="bf8f0483069b443c8ca25f1b5f3d129b" LocationId="bf8f0483069b443c8ca25f1b5f3d129b" Position="-1" DataCreator="Thomson" DataClass="Metadata" DataName="SomeBoolean" DataVersion="1" DataType="boolean" DataLength="2" Data_boolean="1"/>
 <ns:Extension ExtensionId="4a3373ef41684e2685790f400d1021a0" MovieId="bf8f0483069b443c8ca25f1b5f3d129b" LocationId="bf8f0483069b443c8ca25f1b5f3d129b" Position="-1" DataCreator="Thomson" DataClass="Metadata" DataName="SomeInteger" DataVersion="1" DataType="i4" DataLength="4" Data_i4="5"/>
 <ns:Extension ExtensionId="96f61e9c534c49e4b3369169ec9116e8" MovieId="bf8f0483069b443c8ca25f1b5f3d129b" LocationId="bf8f0483069b443c8ca25f1b5f3d129b" Position="-1" DataCreator="Thomson" DataClass="Metadata" DataName="SomeFloat" DataVersion="1" DataType="r4" DataLength="4" Data_r4="3.14"/>
 <ns:Extension ExtensionId="9c3d55b81c4e4760a40411f60242be60" MovieId="bf8f0483069b443c8ca25f1b5f3d129b" LocationId="bf8f0483069b443c8ca25f1b5f3d129b" Position="-1" DataCreator="Thomson" DataClass="Metadata" DataName="SomeName" DataVersion="1" DataType="string" DataLength="18" Data_string="SomeValue"/>
 <ns:Extension ExtensionId="b8ec96ab0a974393b5e9c6a1793fde78" MovieId="bf8f0483069b443c8ca25f1b5f3d129b" LocationId="bf8f0483069b443c8ca25f1b5f3d129b" Position="-1" DataCreator="Thomson" DataClass="Metadata" DataName="SomeDate" DataVersion="1" DataType="dateTime" DataLength="8" Data_dateTime="2008-03-10T14:43:54.000"/>
 </ns:Data>
</Select>
}}}
The commands listed here are either implemented or stubbed (returning only a command acknowledgment) as indicated. Each command is identified by its command code and name. 

Information listed here is intended to specify the minimum requirements for each command. Unless otherwise specified, the server implements all the capabilities for each command as specified in the Sony documentation. For more information, refer to the second edition or later of the //Sony Videocassette Recorder Bvw Series Protocol Manual// (part number 9-977-544-11). 

''Command list:''
The following table lists the BVW protocol commands. The server returns an ACK response to each command, unless otherwise indicated. 

|''Command''|''Name''|''Return''|
|0X.11|Device Type Request|The controller can distinguish Profile from ~BVW-75: If 00.11 is sent, returns 20.25 or 21.25 (for 525- or 625-line opera¬tion respectively). If 01.11.00 is sent, returns D8.00 (disk recorder). This allows the controller to decide whether to cue-up by a combi¬nation of ~FastForward/Rewind and ~CueUpWithData (faster with ~BVW-75) or ~CueUpWithData (faster with Profile). |
|20.00|Stop||
|20.01|Play||
|20.02|Record||
|20.04|Standby Off|Stubbed. Acknowledges command only.|
|20.05|Standby On|Stubbed. Acknowledges command only.|
|20.0F|Eject||
|20.10|Fast Forward||
|2X.11|Jog Forward||
|2X.12|Variable Forward||
|2X.13|Shuttle Forward||
|20.20|Rewind||
|2X.21|Jog Reverse||
|2X.22|Variable Reverse||
|2X.23|Shuttle Reverse||
|20.30|Preroll||
|24.31|Cue Up With Data|Matches behavior of ~BVW-75 as follows: After start-up, or after the displayed timecode is changed from the "Options/Select Timecode ..."dialog box, ~CueUpWithData cues up using the following time codes: VITC if VITC recorder is displayed LTC if LTC recorder is displayed Timer1 otherwise After ~TimerModeSelect (41.36) has been sent, ~CueUpWithData cues up using the following time codes: VITC if 41.36.00 is sent and VITC is displayed LTC if 41.36.00 is sent and anything else is displayed Timer1 if 41.36.01 is sent|
|21.38|Program Play +||
|21.39|Program Play ¬||
|20.52|Tension Release|Stubbed. Acknowledges command only.|
|20.60|Full EE Off|Panel indicator (PB/EE) changes to match.|
|20.61|Full EE On|Panel indicator changes to EE only. This causes timecode, video, and audio to go to EE in the BVW (insert-edit) controller, and video and audio to go to EE in the BVW controller.|
|20.63|Selected EE On||
|20.64|Edit Off||
|20.65|Edit On|
|44.00|Timer-1 Preset||
|44.04|Time Code Preset||
|44.05|User Bit Reset||
|44.08|Timer-1 Reset||
|40.10|In Entry||
|40.11|Out Entry||
|44.14|In Preset||
|44.15|Out Preset||
|40.20|In Reset||
|40.21|Out Reset||
|40.24|In Recall||
|40.25|Out Recall||
|4X.30|Edit Preset||
|44.31|Preroll Time Preset||
|41.34|Head Select|Stubbed. Acknowledges command only.|
|41.36|Timer Mode Select||
|40.40|Auto Mode Off|Stubbed. Acknowledges command only.|
|40.41|Auto Mode On|Stubbed. Acknowledges command only.|
|61.0C|Current Time Sense||
|60.10|In Data Sense||
|60.11|Out Data Sense|
|61.20|Status Sense|
|6X.30|Edit Preset Sense|
|60.31|Preroll Time Sense|
|60.36|Timer Mode Sense|
|A0:01|Enable Clip Loading|
|A0:02|Disable Clip Loading|
|A0:10|Clip Length Request|Clip length response in the form 00:00:00:00.|
|A8:12|Set Clip Length|
|A0:20|Find First Clip Request|Clip name.|
|A0:21|Find Next Clip Request|Clip name.|
|AX:30|Load Clip||
|AX:40|New Clip||
|AX:50|Delete Clip||
The code below shows how to create a basic connection to a K2's ~AppService.


{{{
[C#] 
// create an AppServerMgr proxy object
AppServerMgrProxy appServerMgrProxy = new AppServerMgrProxy();

// set the host name of the K2 server
string host = "K2machinename";
appServerMgrProxy.SetHost(host);

// set the user credentials
string username = "username";
string password = "password";
string domain = "domain";
appServerMgrProxy.SetUserCredentials(username, password, domain, false);

// connect to the AppServerMgr
if ( !appServerMgrProxy.Connect() )
 Console.WriteLine("ERROR: Could not connect to AppService on " + host + ".");
else
 Console.WriteLine("Successfully connected to AppService.");

// ... do some work ...

// disconnect
appServerMgrProxy.Disconnect();
}}}

{{{
[C++] 
// Initialize the COM library
CoInitialize(NULL);

// create an AppServerMgr proxy object
IAppServerMgrProxyPtr spAppServerMgrProxy (__uuidof(AppServerMgrProxy));

// set the host name of the K2 server
_bstr_t bstrHost("K2machinename");
hr = spAppServerMgrProxy->SetHost(bstrHost);

// set the user credentials
_bstr_t bstrUsername("username");
_bstr_t bstrPassword("password");
_bstr_t bstrDomain("domain");
VARIANT_BOOL encrypted(FALSE);
hr = spAppServerMgrProxy->SetUserCredentials( bstrUsername, bstrPassword, 
 bstrDomain, encrypted);

// connect to the AppServerMgr
VARIANT_BOOL retVal;
hr = spAppServerMgrProxy->Connect(&retVal);
if ( !retVal )
 printf("ERROR - couldn't connect to the AppServerMgr\n");

// ... do some work ...

// disconnect
spAppServerMgrProxy->Disconnect(&retVal);
}}}
Go to [[CS-Script - The C# Script Engine|http://www.members.optusnet.com.au/~olegshilo/]] website.

''~CS-Script'' (pronounced C sharp script) is a CLR (Common Language Runtime) based scripting system which uses ~ECMA-compliant C# as a programming language. It combines the power and richness of C# with the flexibility of a scripting system.

The C# samples below take advantage of ''~CS-Script'' by providing K2 code that will run from a single C# file. For instance, you could open a DOS prompt and run a script as easily as this:
{{{
C:\> cscs.exe PlayClip.cs
}}}

To use these samples you will first need to setup your machine:

1. If you are writing code on a PC rather than the K2, [[install the Control Point software|http://www.grassvalley.com/downloads/servers/k2_3.2.21.746.html]] on your PC.

2. Next install ''~CS-Script'' software:
* [[Download cs-script.zip|zip/cs-script.zip]]. Extract it to "C:\Program Files". It will create a new directory called "C:\Program Files\cs-script".
* Next run "C:\Program Files\cs-script\css_config.exe". This will update system environment variables and bring up the configuration console. Press the "Close" button.
* To uninstall, run "C:\Program Files\cs-script\config.bat" and press 'Deactivate' button on the 'General' tab in the configuration console. It will undo any changes made by ~CS-Script installation.
3. (Optionally) Install a text editor that is good for code editing (recommendation: [[Notepad++|http://notepad-plus.sourceforge.net/uk/site.htm]].)

4. Save any of the examples below to your "c:\profile" directory, then at a DOS prompt run: "cscs.exe <filename>"

''Summit Samples:''
|[[LivePlay.cs|CS-Script/LivePlay.cs]]|shows how to play a live recording clip within 0.5 second of the record position.|
|[[FlyingMixEffects.cs|CS-Script/FlyingMixEffects.cs]]|shows how to create flying mix effects between the foreground and background contexts of a channel.|
|[[PlaylistMixEffects.cs|CS-Script/PlaylistMixEffects.cs]]|shows how to create a playlist with section and event mix effects transitions.|

''Samples:'' ([[click here to download all samples|zip/CSSamples.zip]])
|''File''|''Description''|
|[[HelloWorld.cs|CS-Script/HelloWorld.cs]]|basic "Hello World" C# script demonstrating command-line arguments, getting input, printing out text, and returning an exit code|
|[[BasicK2Connection.cs|CS-Script/BasicK2Connection.cs]]|script showing each K2 connection steps. It shows you the basic calls you need to get started|
|[[K2ScriptTemplate.cs|CS-Script/K2ScriptTemplate.cs]]|a better K2 connection script template that encapsulates several of the connection calls in one connection object. makes for easier code reading.|
|[[GetSystemStatus.cs|CS-Script/GetSystemStatus.cs]]|get system status|
|[[LogMessage.cs|CS-Script/LogMessage.cs]]|log a message|
|[[GetListOfClips.cs|CS-Script/GetListOfClips.cs]]|gets a list of clips in a bin|
|[[PlayClip.cs|CS-Script/PlayClip.cs]]|play a clip|
|[[RecordClip.cs|CS-Script/RecordClip.cs]]|record a clip|
|[[ScheduledPlay.cs|CS-Script/ScheduledPlay.cs]]|play a clip at a scheduled time|
|[[ScheduledRecord.cs|CS-Script/ScheduledRecord.cs]]|record a clip at a scheduled time|
|[[CopyClip.cs|CS-Script/CopyClip.cs]]|copy a clip|
|[[DeleteClip.cs|CS-Script/DeleteClip.cs]]|delete a clip|
|[[TransferaClip.cs|CS-Script/TransferaClip.cs]]|transfer a clip|
|[[GetVersion.cs|CS-Script/GetVersion.cs]]|get system version information|
|[[GetDiskSpace.cs|CS-Script/GetDiskSpace.cs]]|get disk space|
|[[GetChannelOwner.cs|CS-Script/GetChannelOwner.cs]]|get channel owner information|
|[[GetAssetProperties.cs|CS-Script/GetAssetProperties.cs]]|get properties of a clip|
|[[GetChannelProperties.cs|CS-Script/GetChannelProperties.cs]]|get channel properties|
|[[CreateBin.cs|CS-Script/CreateBin.cs]]|create a bin|
|[[GetXmlChanges.cs|CS-Script/GetXmlChanges.cs]]|get XML change information|
|[[ClipCacheCalls.cs|CS-Script/ClipCacheCalls.cs]]|demonstrates calls needed to efficiently build a clip cache|
|[[EnumerateVolumes.cs|CS-Script/EnumerateVolumes.cs]]|enumerate the volumes|
|[[MonitorTransfers.cs|CS-Script/MonitorTransfers.cs]]|monitor transfers|
|[[PlayList.cs|CS-Script/PlayList.cs]]|create a play list|
|[[RecordAndRenameClip.cs|CS-Script/RecordAndRenameClip.cs]]|record clip, get editor, rename clip|
|[[viewmovie.cs|CS-Script/viewmovie.cs]]|view movie attributes|
|[[GetTimecode.cs|CS-Script/GetTimecode.cs]]|get the current timecode of a player|
|[[InsertEditTwoHead.cs|CS-Script/InsertEditTwoHead.cs]]|demonstrates how to do insert edits and use two heads for playback|
|[[PlayerCreateSubclip.cs|CS-Script/PlayerCreateSubclip.cs]]|create a subclip from a clip loaded in a player|
|[[MediaMgrCreateSubclip.cs|CS-Script/MediaMgrCreateSubclip.cs]]|create a subclip from a clip NOT loaded in a player|
|[[ImportDeleteTracks.cs|CS-Script/ImportDeleteTracks.cs]]|shows how to import, delete, move, and label tracks in a clip|
|[[AddAncillaryData.cs|CS-Script/AddAncillaryData.cs]]|shows how to merge, replace, and add ancillary data to a clip|
|[[LoadSaveConfig.cs|CS-Script/LoadSaveConfig.cs]]|shows how to load a configuration from or save a configuration to a local file.|
|[[SetAssetARC.cs|CS-Script/SetAssetARC.cs]]|set aspect ratio conversion on a clip-by-clip basis.|
For the ~MediaMgr calls ~EnumerateVolumes, ~EnumerateBins, and ~EnumerateAssets, you need to call ~CloseResults on the cookie when you are finished using it. Your ~MediaMgr object only gets ten database selectors to work with. Everytime time you make an "Enumerate" call you use one of the selectors. Once you run out of selectors, your "Enumerate" calls will fail. You will also see “Too many open select statements: limit is 10” errors in the log. Calling ~CloseResults frees the selector and makes it usable to ~MediaMgr again.

Here is an example using ~EnumerateBins:

{{{
[C++]
IMediaMgrPtr spMediaMgr;
hr = spAppServer->CreateMediaMgr(m_bstrAppName, &spMediaMgr);

_bstr_t volume("V:");
long count = 0;
long cookie = 0;

hr = spMediaMgr->EnumerateBins(volume, &count, &cookie);
printf("hr = 0x%x, # of bins = %d\n\n", hr, count);

// ... loop code to GetXmlResults ...

// make sure your close the results!
hr = spMediaMgr->CloseResults(cookie);
}}}

See [[Get a list of clips]] for another enumeration example.
<<forEachTiddler
 where
 'tiddler.tags.contains("ChangeNotice")'
 sortBy
 'tiddler.data("date")'
 descending
 script
'function writeBugReportLine(tiddler, index) 
 {
 var result =""; 

 if (index ==0) 
 {
 result +="|background-color(#314184):!Date|background-color(#314184):!Title|background-color(#314184):!Build with Fix|\n";
 }

 result += "|" + tiddler.data("date") + "|" +
 "[[" + tiddler.title + "]]|";

 if ( tiddler.data("buildsWithFix") != null )
 result += tiddler.data("buildsWithFix") + "|\n";
 else
 result += "|\n";

 return result;
 }'
write
 'writeBugReportLine(tiddler,index)'
>>
<html>
<style>
.rolodex table {
border: 1px solid;
background-color:#DDDDDD;
}

.rolodex tr, .rolodex td {
border: 0px solid;
}

textarea{
font-family:Helvetica,sans-serif;
font-size:100%;
}

</style>
<span class="rolodex">
 <table>
 <tr>
 <td colspan="6"><b>Change Notice</b></td>
 </tr>

 <tr>
 <td colspan="2">Date (yyyy-mm-dd):&nbsp;<input name=date type=text style="width:40%" /></td>
 </tr>

 <tr>
 <td colspan="6">Description:</td>
 </tr>
 <tr>
 <td colspan="6">
 <textarea name=description rows="10" cols="135" style="width:99%" ></textarea>
 </td>
 </tr>

 <tr>
 <td colspan="1">Builds with Fix:</td>
 <td colspan="2">CheckIn Time/Branch:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
 <td colspan="1">Associated CR's:</td>
 </tr>
 <tr>
 <td colspan="1"><textarea name=buildsWithFix rows="3" cols="20" style="width:95%" ></textarea></td>
 <td colspan="2"><textarea name=checkinTimeBranch rows="3" cols="20" style="width:95%" ></textarea></td>
 <td colspan="1"><textarea name=assocCRs rows="3" cols="20" style="width:95%" ></textarea></td>
 </tr>
</table>
</span>
</html>
|>|>|>|''Application [supplied] Status''|
|!Property|!Unmanaged Type|!Managed Type|!Description|!Notes|
|text0|BSTR|System.String|Custom text string generally provided by the application that owns the channel||
|text1|BSTR|System.String|Custom text string generally provided by the application that owns the channel||
|text2|BSTR|System.String|Custom text string generally provided by the application that owns the channel||
|text3|BSTR|System.String|Custom text string generally provided by the application that owns the channel||
|statusTemplate|BSTR|System.String|Status templates string last set with a call to ~IChanStatus::put_~StatusTemplate||
|thumbnailStatus|BSTR|System.String|Indicates the status of the thumbnail provided by the application that owns the channel. Values include: Empty, New, Same, and Unknown. The status is initialized to “Unknown”. It is “New” just after a call to ~IChanStatus::~SetThumbnail. It is “Same” after the first time the thumbnail status is returned by this process. It is “Empty” after a call to ~IChanStatus::~ClearThumbnail.||
|thumbnail|BYTE SAFEARRAY|System.Object[]|Jpeg thumbnail.||
|totalstorage|long|System.Int32|total disk space in ~MBytes||
|remainingstorage|long|System.Int32|remaining disk space in ~MBytes||
|pcntstorageremaining|long|System.Int32|percentage of disk space remaining||
|timeremainingstr|BSTR|System.String|time remaining on disk displayed as timecode value (depends on how channel is configured)||
|timeremaining|long|System.Int32|time remaining in fields (depends on how channel is configured)||

|>|>|>|''Channel Status''|
|!Property|!Unmanaged Type|!Managed Type|!Description|!Notes|
|channelName|BSTR|System.String|Name of this channel (K2: C1, C2, C3, C4; ~MSeries: R1, P1, R2, P2)||
|channelType|BSTR|System.String|Values include Player, Recorder, and ~PlayerRecorder.||
|ownerName|BSTR|System.String|Name of the application that has allocated this channel||
|videoFormat|long|System.Int32|Channel’s configured video format. Enumerated values include: {{{Format525_60Hz_2To1 = 0, Format625_50Hz_2To1, Format720_59_94Hz_1To1, Format720_60Hz_1To1, Format1080_23_98Hz_1To1, Format1080_24Hz_1To1, Format1080_25Hz_1To1, Format1080_29_97Hz_1To1, Format1080_30Hz_1To1, Format1080_25Hz_2To1, Format1080_29_97Hz_2To1, Format1080_30Hz_2To1, Format1035_30Hz_2To1, Format1035_29_97Hz_2To1, VideoFormat_NA, Unknown}}}.||
|videoFormatStr|BSTR|System.String|String representation of channel’s configured video format. Values include: {{{Format525_60Hz_2To1, Format625_50Hz_2To1, Format720_59_94Hz_1To1, Format720_60Hz_1To1, Format1080_23_98Hz_1To1, Format1080_24Hz_1To1, Format1080_25Hz_1To1, Format1080_29_97Hz_1To1, Format1080_30Hz_1To1, Format1080_25Hz_2To1, Format1080_29_97Hz_2To1, Format1080_30Hz_2To1, Format1035_30Hz_2To1, Format1035_29_97Hz_2To1, VideoFormat_NA, Unknown.}}}||
|fieldsPerFrame|long|System.Int32|For interlaced video formats this value is 2. For progressive scan, the value is 1.||
|ganged|BOOL|System.Boolean|whether or not the channel is ganged||
|gangindex|long|System.Int32|-1 = unganged, 0 = gang 1, 1 = gang 2||
|singlecontroller|BOOL|System.Boolean|whether or not a single channel controls the gang||
|consolidateaudio|BOOL|System.Boolean|whether or not to record audio from more than one channel||
|consolidatevideo|BOOL|System.Boolean|whether or not to record video from more than one channel||

|>|>|>|''Asset Status''|
|!Property|!Unmanaged Type|!Managed Type|!Description|!Notes|
|assetname|BSTR|System.String|Asset name.|
|assetUri|BSTR|System.String|Asset identifier in URI format. The URI string takes the following form: {{{edl/cmf//<machine>/<volume>/<bin>/<name>}}}||
|editName|BSTR|System.String|When the channel is playing a List asset, this is the name of the edit currently being played.||
|nextEditName|BSTR|System.String|When the channel is playing a List asset, this is the name of the next edit to be being played. This status value is currently always blank.||
|sectionName|BSTR|System.String|When the channel is playing a List asset, this is the name of the section currently being played.||
|nextSectionName|BSTR|System.String|When the channel is playing a List asset, this is the name of the next section to be being played. This status value is currently always blank.||

|>|>|>|''Timeline Status''|
|!Property|!Unmanaged Type|!Managed Type|!Description|!Notes|
|durationStr|BSTR|System.String|Timecode representation of the channel timeline’s maxPos – minPos value. This is the amount of playable material on the timeline.|r|
|countdownStr|BSTR|System.String|Timecode representation of the maxPos – position value. This is the amount of play time left on the timeline.|r|
|percentComplete|float|float|Channel timeline’s maxPos – position expressed as a percent. If the timeline is being used for a crash record, then the percent complete gets to 100.0 every 10 seconds and starts over at 0.0.|r|
|position|long|System.Int32|Timeline position in fields. This value will always be 0 or greater.|r|
|portState|long|System.Int32|Enumerated value indicating the state of the timeline:<br />idle = 0<br />cued for playback = 1<br />cued for record = 2<br />playing = 3<br />recording = 4.|r|
|shuttleSpeed|float|float|Current rate. Normal play speed is 1.0.|r|
|sectionCountdownStr|BSTR|System.String|Timecode representation of the end-of-the-current-section – position value. This is the amount of play time left in the current section.|r|
|eventCountdownStr|BSTR|System.String|Timecode representation of the end-of-the-current-edit – position value. This is the amount of play time left in the current section.|r|
|sectionPercent|float|float|Channel timeline’s end-of-the-current-section – position value expressed as a percent.|r|

|>|>|>|''Video Status''|
|!Property|!Unmanaged Type|!Managed Type|!Description|!Notes|
|fieldsize|long|System.Int32|Compressed size of the last played field in bytes.|r|
|pixCoding|BSTR|System.String|I, B, or P|r|

|>|>|>|''Audio Status (track 0)''|
|!Property|!Unmanaged Type|!Managed Type|!Description|!Notes|
|inputGain0|float|float|Audio input gain value. This is used to adjust the input audio level. Units are in dBU. Range is –40.0 to 20.0.|r|
|outputGain0|float|float|Audio output gain value. This is used to adjust the output audio level. Units are in dBU. Range is –40.0 to 20.0.|r|
|audioLevel0|float|float|Current audio level after gain value has been applied. Units are in dBU.|r|

|>|>|''Audio Status (track 1)''|
|!Property|!Unmanaged Type|!Managed Type|!Description|!Notes|
|inputGain1|float|float|Audio input gain value. This is used to adjust the input audio level. Units are in dBU. Range is –40.0 to 20.0.|r|
|outputGain1|float|float|Audio output gain value. This is used to adjust the output audio level. Units are in dBU. Range is –40.0 to 20.0.|r|
|audioLevel1|float|float|Current audio level after gain value has been applied. Units are in dBU.|r|

|>|>|>|''Timecode Status''|
|!Property|!Unmanaged Type|!Managed Type|!Description|!Notes|
|timecodeStr|BSTR|System.String|When EE or recording, ~TimecodeStr is the status value from the first timecode resource’s current timecode source (VITC, LTC, or INT). When playing back, ~TimecodeStr represents the timecode media value.|r|

Notes:
r – Status is available in a range of values (~IChanStatus::~GetStatusRange)
''[[Click here|Basic Connection]]'' for example code on how to connect to ~AppService. See [[other code examples|Examples]], too.
[img[General AppService call sequence|images/CallSequence.png]]
''Client application ~AppService call sequence''

''Proxies:''
Client applications interact with the K2 via "proxy" objects. These are objects in the client application that represent the real objects in the K2. The proxy object's job is to simplify calls and to encapsulate house-keeping tasks so that the client application does not need to worry about it.

''Here are the basic steps for a client application to connect to a K2 and do work:''

''1. Connect to a K2's ~AppService:''
1a. Create an ~AppServerMgrProxy object in the client application. 
1b. Tell it which K2 you want to connect to by calling the "~SetHost" function.
1c. Pass in user credentials by calling the "~SetUserCredentials" function.
1d. Call "Connect()". It returns true or false.

''2. Create an ~AppServer:''
Once connected, create an ~AppServer on the K2 by calling "~CreateAppServer". This will create an AppServer on the K2 and return an ~AppServerProxy object back to the client application.

''3. Create subsystem objects:''
Now that you have an ~AppServer for your client, create the subsystem object(s) that you need by calling "Create<Object>" (i.e. ~CreateController, ~CreateLog, etc.). This will create the object on the K2 and return an <Object>Proxy object to the client.

''4. Make K2 calls:''
Make calls on subsystem proxy objects to control or retrieve information from the K2.

''5. Cleanup:''
When finished, cleanup and delete the ~AppServer and subsystem objects by calling ~CloseConnection on the ~AppServerProxy. 
Background: #fff
Foreground: #000
PrimaryPale: #8cf
PrimaryLight: #18f
PrimaryMid: #04b
PrimaryDark: #014
SecondaryPale: #f8fff4
SecondaryLight: #F8FFF4
SecondaryMid: #048
SecondaryDark: #841
TertiaryPale: #eee
TertiaryLight: #ccc
TertiaryMid: #999
TertiaryDark: #666
Error: #f88
[[PageTemplate]]
|>|>|[[SiteTitle]] - [[SiteSubtitle]]|
|[[MainMenu]]|[[DefaultTiddlers]] <html><br><br><br><br></html>[[ViewTemplate]] <html><br><br></html> [[EditTemplate]]|[[SideBarOptions]]|
|~|~|[[OptionsPanel]]|
|~|~|[[AdvancedOptions]]|
|~|~|<<tiddler Configuration.SideBarTabs>>|

''StyleSheet:'' [[StyleSheetColors]] - [[StyleSheetLayout]] - [[StyleSheetPrint]]

[[SiteUrl]]
[[SideBarTabs]]
|[[TabTimeline]]|[[TabAll]]|[[TabTags]]|<<tiddler Configuration.TabMore>>|
|>|[[TabMore]]|
|TabMoreMissing|TabMoreOrphans|
Often you want to synchronize the playout or recording of a channel or multiple channels at a specific time. Context Triggers and Scheduled Context Conditions are used for this.

To schedule events to happen at specific triggers or conditions, issuing the following sequence of commands:

1. Cue the asset by calling a cue command. The clip must be cued before your can schedule or commit a change.
2. call "~BeginContextChange” command
3. make the calls you want to happen at the trigger or condition (i.e. Play() )
4. call "~CommitContextChange" or “~ScheduleContextChange” command.

''@@color(red):Note:@@'' see the [[C# script sample scripts section|CS-Script%20Sample%20Scripts]] and look at [[InsertEditTwoHead.cs|CS-Script/InsertEditTwoHead.cs]] for different Context Trigger and Scheduled Context examples.
!!Context Trigger types:
''"asap"'' – Used to apply a context change to the active context or a preview context. Committing with ASAP will make a preview context active. When cueing to a new position with ASAP, the decoder will wait until the correct picture is fetched.

''"now"'' – Used to apply a context change to the active context or a preview context. Committing with NOW will make a preview context active though, generally should only be used with the active context. When cueing to a new position with NOW, the decoder will immediately push out a picture, event if it is not the correct one. Use this context trigger when committing the first context change if you are not immediately playing. The still picture will pushed out sooner. Use the NOW context trigger when implementing a “scrub bar” and you want to push out a lot of pictures rapidly, so don’t care if they are the exact picture. Eventually the decoder will show the appropriate picture.

''"manual"'' – Used to apply a context change to a preview context without activating it. The preview context will never be activated if its last trigger was MANUAL.

''"follow"'' – Used to apply a context change to a preview context without activating it. When a preview context’s last trigger was FOLLOW, then it will automatically be activated when the active context reaches its end limit.

The code below illustrates how to play a second clip immediately after a clip:
{{{
// C# Code
// create a player and a background preview context
string channel = "C1";
bool isNewController = false;
ISimpleController controller = connection.AppServer.CreateController(
	appName, channel, out isNewController);
ISimplePlayerRecorder player = (ISimplePlayerRecorder) controller;
ITwoHeadPlayerRecorder preview = (ITwoHeadPlayerRecorder) controller;

// load and start playing clip
player.Load("edl/cmf//local/V:/default/clip1");
player.Play();
						
// while main context is playing, load preview context with a second clip
preview.LoadPreview("edl/cmf//local/V:/default/clip2");

// setup a context change so that the second clip follows the first
preview.BeginPreviewContextChange();
preview.CueStart(); // cue to some position in clip
preview.Play();

// tell preview context to go active after primary context finishes (i.e. follow)
preview.CommitContextChange("follow"); 

// wait awhile...
Thread.Sleep(45000);

// stop and eject the clip
player.Stop();
player.Eject();

// close the channel
controller.CloseChannel();
}}}

!!Scheduled Context Conditions:
Scheduled context changes are always applied with an ASAP trigger when the scheduled condition is met. They include:

''"tickcount"'' tickValue (UINT) – A channel’s tickcount is incremented every field from the time it was started. The condition is met when the channel’s tickcount reaches the given value.

''"gpi"'' gpiInputMask (UINT) – The GPI condition is met when one or more GPI inputs indicated in the gpiInputMask are asserted. The GPI inputs in this mask must have been associated with the channel in the config utility.

''"timeofday"'' timecode (string) – This condition is met when the time of day clock value equals the given timecode string. This is useful when you want to schedule multiple channels to a single clock source. Use ~AppCenter's System Config panel for setting the ~TimeOfDay source.

''"vitc"'' timecode (string) – This condition is met when the VITC value on a channel equals the given timecode string.

''"ltc"'' timecode (string) – This condition is met when the LTC value on a channel equals the given timecode string.

''"ancvitc"'' timecode (string) – This condition is met when the ANCVITC value on a channel equals the given timecode string.

''"ancltc"'' timecode (string) – This condition is met when the ANCLTC value on a channel equals the given timecode string.

''"int"'' timecode (string) – This condition is met when the internal timecode source equals the timecode string.

''"gen"'' timecode (string) – Same as INT. This condition is met when the generated timecode source equals the timecode string.

''"gang"'' channelMask(UINT) – The context change is applied simultaneously to all channels indicated in the given channel mask. It is the application’s responsibility to have correctly set up all the channels appropriately.

''"transition"'' duration(int), videoTransition(int), audioTransition(int), matte(UINT) – This condition activates a preview context using the given transition parameters. It is also called a “Flying M/E”. The condition is an array of four values. This condition is only available on K2 Summit.

The code below illustrates how to start playing a clip at a specific timecode. Note: you can specify what the source of Time Of Day clock is for the K2 Client from ~AppCenter’s System Configuration panel. For instance you might set the Time Of Day clock to an LTC input. In this case also make sure that you have a valid LTC source connected to the K2 Client.
{{{
// C# Code
// create a controller
bool isNewController = false;
ISimpleController icontroller = connection.AppServer.CreateController(
	appName, channel, out isNewController);

// cast it to a player recorder and load a clip
ISimplePlayerRecorder player = (ISimplePlayerRecorder) icontroller;
player.Load("edl/cmf//local/V:/default/Clip");

// must cue clip before you can schedule it!
player.CueStart();

// begin context change, telling it to play at a particular timecode
player.BeginContextChange();
player.Play();
player.ScheduleContextChange("timeofday", "09:00:00.00");

// to cancel schedule change
// player.CancelScheduledChange("timeofday", "09:00:00.00");

// wait awhile...
Thread.Sleep(45000);

// stop and eject the clip
player.Stop();
player.Eject();

// close the channel
icontroller.CloseChannel();
}}}


~ControlPoint PC system requirements are as follows:

|''Requirements''|''Comments''|
|Operating System|Microsoft Windows XP Professional, Service Pack 2 (Must be a U.S. version)|
|RAM|Minimum 512 MB, 1 GB recommended|
|Graphics acceleration|Must have at least 128M memory|
|Processor|Pentium 4 or higher class, 2 ~GHz or greater|
|Hard disk space|400 MB|
|Microsoft .NET Framework|Version 1.1 and 2.0|
|Java JRE|1.3.1_12 and 1.4.2_05 or higher. Required for HP Ethernet Switch Configuration interface, which is used for K2 Storage Systems (external storage).|
|XML|Microsoft XML 4 Service Pack 2 is required. You can install it from the msxml4sp2 file on the K2 System Software CD.|
The sample code below shows how to copy an asset. An ~AssetExists call is added to first make sure that the source clip exists.

{{{
[C#]
// create a mediamgr object
IMediaMgr mediaMgr = iappServer.CreateMediaMgr(appName);

// if the clip does not exist, print error msg and return
if ( !mediaMgr.AssetExists("edl/cmf//local/V:/default/Clip") )
{
 Console.WriteLine("Clip V:/default/Clip doesn't exist!");
 return;
}

// copy clip 
mediaMgr.CopyAsset("edl/cmf//local/V:/default/Clip", 
 "edl/cmf//local/V:/default/Clip_Copy");

// cleanup
mediaMgr.Dispose();
}}}
{{{
[C++]
// create a mediamgr object
IMediaMgrPtr spMediaMgr;
hr = spAppServer->CreateMediaMgr(_bstr_t("YourApplicationName"), 
 &spMediaMgr);

// if the clip does not exist, print error msg and return
if ( !spMediaMgr->AssetExists( _bstr_t("edl/cmf//local/V:/default/Clip") )
{
 printf("Clip V:/default/Clip doesn't exist!\n");
 return;
}

// copy clip
hr = spMediaMgr->CopyAsset( _bstr_t("edl/cmf//local/V:/default/Clip"), 
 _bstr_t("edl/cmf//local/V:/default/Clip_Copy") );
}}}
The sample code below shows how to create a bin. Bins contain assets (clips, playlists, etc.)


{{{
[C#]
// create a mediamgr object
IMediaMgr mediaMgr = iappServer.CreateMediaMgr(appName);

// create the bin V:/testbin
mediaMgr.CreateBin("V:", "testbin");

// cleanup
mediaMgr.Dispose();
}}}
{{{
[C++]
// create a mediamgr object
IMediaMgrPtr spMediaMgr;
hr = spAppServer->CreateMediaMgr(_bstr_t("YourApplicationName"), 
 &spMediaMgr);

// create the bin V:/testbin
spAppServer->CreateBin( _bstr_t("V:"), _bstr_t("testbin") );
}}}
Below is example C# and C++ code for creating a basic playlist. This playlist has two sections that each contain two events. See [[Playlists, sections, and events|Playlists sections and events]] for an explanation on playlists.

A C# sample script can also be found on the [[C# Sample Scripts|CS-Script Sample Scripts]] page.
{{{
[C#]
// create a two-head player recorder
bool isNewController = false;
ITwoHeadPlayerRecorder player = (ITwoHeadPlayerRecorder) appServer.CreateController(
 "YourApplicationName", "C1", out isNewController);

// set loop play mode to false
player.LoopPlayMode = false;

// enable list pauses
player.SetChannelProperty("listpauses", "true");

// enable list repeats
player.SetChannelProperty("listrepeats", "true");

// set current bin
player.SetCurrentBin("V:", "default");

// generate a unique name for the playlist (i.e. playlist, playlist_0, etc.)
string playlist = player.GenerateUniqueName ("playlist", "_");

// eject the play first to make sure we're clean
player.Eject();

// generate a new clip name (which also loads it)
player.New(playlist);

// get the editor
IEventEditor editor = (IEventEditor) player.GetEditor();

// insert the first section
string section1 = editor.InsertSection("");

// set the starting timecode
editor.SetStartingTimecode("01:00:00,00");

// insert several play events and get properties back 
string editID_A = editor.InsertPlayEvent(@"edl/cmf//local/V:/default/ClipA", section1, 0, "");
object[] array = (object[]) editor.GetEventProperty (editID_A, "name+startpos+duration");
// your code to do something with event properties

string editID_B = editor.InsertPlayEvent(@"edl/cmf//local/V:/default/ClipB", section1, 0, "");
array = (object[]) editor.GetEventProperty (editID_B, "name+startpos+duration");
// your code to do something with event properties

// insert another section
string section2 = editor.InsertSection("");

// insert several more play events and get properties back
string editID_C = editor.InsertPlayEvent(@"edl/cmf//local/V:/default/ClipC", section2, 0, "");
array = (object[]) editor.GetEventProperty (editID_C, "name+startpos+duration"); 
// your code to do something with event properties

string editID_D = editor.InsertPlayEvent(@"edl/cmf//local/V:/default/ClipD", section2, 0, "");
array = (object[]) editor.GetEventProperty (editID_D, "name+startpos+duration"); 
// your code to do something with event properties

// play the list for awhile
player.Play();
Thread.Sleep(30000);
player.Stop();

// delete events
editor.DeleteEvent(editID_C);
editor.DeleteEvent(editID_D);

// delete section 2 
editor.DeleteSection(section2);

// note: you cannot delete all sections. Since this is a playlist
// it will force you to keep at least one section in it.
// editor.DeleteSection(section2); // this would error

// eject the clip
player.Eject(); 

// close the channel
player.CloseChannel();

// cleanup
editor.Detach();
}}}
{{{
[C++]
short nIsNewController;
ISimpleControllerPtr spController;
_variant_t varChanNum(m_bstrChannel);

// create the controller and cast it to different interfaces
HRESULT hr = spAppServer->CreateController(_bstr_t("YourApplicationName"), _bstr_t("C1"), 
 &nIsNewController, &spController);
//printf("CreateController hresult = 0x%x\n", hr);

ISimplePlayerRecorderPtr spPlayer = (ISimplePlayerRecorderPtr) spController;
ITwoHeadPlayerRecorderPtr spTwoHeadPlayer = (ITwoHeadPlayerRecorderPtr) spController;

// set loop play mode to false
VARIANT_BOOL loopmode(FALSE);
hr = spPlayer->put_LoopPlayMode(loopmode);

// enable list pauses
hr = spController->SetChannelProperty(_bstr_t("listpauses"), _variant_t("true"));

// enable list repeats
hr = spController->SetChannelProperty(_bstr_t("listrepeats"), _variant_t("true"));

// set current bin
hr = spController->SetCurrentBin(_bstr_t("V:"), _bstr_t("default"));
 
// generate a unique name for the playlist (i.e. playlist, playlist_0, etc.)
BSTR playlist;
hr = spController->GenerateUniqueName (_bstr_t("playlist"), _bstr_t("_"), &playlist);

// eject the play first to make sure we're clean
hr = spPlayer->Eject();

// create the new playlist (which also loads it)
hr = spPlayer->New(playlist);

// get an editor and cast it to an event editor
ISimpleEditorPtr spEditor;
hr = spPlayer->GetEditor(&spEditor);
IEventEditorPtr spEventEditor = (IEventEditorPtr) spEditor;

// insert the first section
BSTR section1;
hr = spEventEditor->InsertSection(_bstr_t(""), &section1);

// set the default starting timecode (this is what AppCenter does).
spEditor->SetStartingTimecode(_variant_t("01:00:00,00"));

// insert several play events and get properties back
BSTR editID_A;
VARIANT varResult;
hr = spEventEditor->InsertPlayEvent(_bstr_t("edl/cmf//local/V:/default/ClipA"), section1, 0, _bstr_t(""), &editID_A);
hr = spEventEditor->GetEventProperty (editID_A, _bstr_t("name+startpos+duration"), &varResult);
// your code to do something with event properties

BSTR editID_B;
hr = spEventEditor->InsertPlayEvent(_bstr_t("edl/cmf//local/V:/default/ClipB"), section1, 0, _bstr_t(""), &editID_B);
hr = spEventEditor->GetEventProperty (editID_B, _bstr_t("name+startpos+duration"), &varResult);
// your code to do something with event properties

// insert another section
BSTR section2;
hr = spEventEditor->InsertSection(_bstr_t(""), &section2);

// insert several more play events and get properties back
BSTR editID_C;
hr = spEventEditor->InsertPlayEvent(_bstr_t("edl/cmf//local/V:/default/ClipA"), section1, 0, _bstr_t(""), &editID_C);
hr = spEventEditor->GetEventProperty (editID_C, _bstr_t("name+startpos+duration"), &varResult); 
// your code to do something with event properties

BSTR editID_D;
hr = spEventEditor->InsertPlayEvent(_bstr_t("edl/cmf//local/V:/default/ClipB"), section1, 0, _bstr_t(""), &editID_D);
hr = spEventEditor->GetEventProperty (editID_D, _bstr_t("name+startpos+duration"), &varResult);
// your code to do something with event properties

// play for awhile
hr = spPlayer->Play();
Sleep(30000);

// eject
hr = spPlayer->Eject();

// close channel
hr = spController->CloseChannel();

// cleanup
hr = spEditor->Detach();
}}}
The code below shows how to create an ~AppServer. You will need to pass in a unique suite name and the name of your client application. See [[Suspending and Closing AppServers]] for an explanation of when an existing ~AppServer would be returned.

{{{
[C#]
bool newConnection = false;
IAppServer iappServer = appServerMgrProxy.CreateAppServer("YourSuiteName", 
 "YourClientApplicationName", out newConnection);

// ... do some work ...

iappServer.CloseConnection(); // or
iappServer.SuspendConnection();
}}}

{{{
[C++] 
short nNewConnection;
IAppServerPtr spAppServer;
hr = spAppServerMgrProxy->CreateAppServer(_bstr_t("YourSuiteName", 
 _bstr_t("YourClientApplicationName"), &nNewConnection, &spAppServer);

// ... do some work ...

spAppServer->CloseConnection(); // or
spAppServer->SuspendConnection();
}}}
/***
|''Name:''|DataTiddlerPlugin|
|''Version:''|1.0.6 (2006-08-26)|
|''Source:''|http://tiddlywiki.abego-software.de/#DataTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
|''TiddlyWiki:''|1.2.38+, 2.0|
|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
!Description
Enhance your tiddlers with structured data (such as strings, booleans, numbers, or even arrays and compound objects) that can be easily accessed and modified through named fields (in JavaScript code).

Such tiddler data can be used in various applications. E.g. you may create tables that collect data from various tiddlers. 

''//Example: "Table with all December Expenses"//''
{{{
<<forEachTiddler
 where
 'tiddler.tags.contains("expense") && tiddler.data("month") == "Dec"'
 write
 '"|[["+tiddler.title+"]]|"+tiddler.data("descr")+"| "+tiddler.data("amount")+"|\n"'
>>
}}}
//(This assumes that expenses are stored in tiddlers tagged with "expense".)//
<<forEachTiddler
 where
 'tiddler.tags.contains("expense") && tiddler.data("month") == "Dec"'
 write
 '"|[["+tiddler.title+"]]|"+tiddler.data("descr")+"| "+tiddler.data("amount")+"|\n"'
>>
For other examples see DataTiddlerExamples.




''Access and Modify Tiddler Data''

You can "attach" data to every tiddler by assigning a JavaScript value (such as a string, boolean, number, or even arrays and compound objects) to named fields. 

These values can be accessed and modified through the following Tiddler methods:
|!Method|!Example|!Description|
|{{{data(field)}}}|{{{t.data("age")}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined {{{undefined}}} is returned.|
|{{{data(field,defaultValue)}}}|{{{t.data("isVIP",false)}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined the defaultValue is returned.|
|{{{data()}}}|{{{t.data()}}}|Returns the data object of the tiddler, with a property for every field. The properties of the returned data object may only be read and not be modified. To modify the data use DataTiddler.setData(...) or the corresponding Tiddler method.|
|{{{setData(field,value)}}}|{{{t.setData("age",42)}}}|Sets the value of the given data field of the tiddler to the value. When the value is {{{undefined}}} the field is removed.|
|{{{setData(field,value,defaultValue)}}}|{{{t.setData("isVIP",flag,false)}}}|Sets the value of the given data field of the tiddler to the value. When the value is equal to the defaultValue no value is set (and the field is removed).|

Alternatively you may use the following functions to access and modify the data. In this case the tiddler argument is either a tiddler or the name of a tiddler.
|!Method|!Description|
|{{{DataTiddler.getData(tiddler,field)}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined {{{undefined}}} is returned.|
|{{{DataTiddler.getData(tiddler,field,defaultValue)}}}|Returns the value of the given data field of the tiddler. When no such field is defined or its value is undefined the defaultValue is returned.|
|{{{DataTiddler.getDataObject(tiddler)}}}|Returns the data object of the tiddler, with a property for every field. The properties of the returned data object may only be read and not be modified. To modify the data use DataTiddler.setData(...) or the corresponding Tiddler method.|
|{{{DataTiddler.setData(tiddler,field,value)}}}|Sets the value of the given data field of the tiddler to the value. When the value is {{{undefined}}} the field is removed.|
|{{{DataTiddler.setData(tiddler,field,value,defaultValue)}}}|Sets the value of the given data field of the tiddler to the value. When the value is equal to the defaultValue no value is set (and the field is removed).|
//(For details on the various functions see the detailed comments in the source code.)//


''Data Representation in a Tiddler''

The data of a tiddler is stored as plain text in the tiddler's content/text, inside a "data" section that is framed by a {{{<data>...</data>}}} block. Inside the data section the information is stored in the [[JSON format|http://www.crockford.com/JSON/index.html]]. 

//''Data Section Example:''//
{{{
<data>{"isVIP":true,"user":"John Brown","age":34}</data>
}}}

The data section is not displayed when viewing the tiddler (see also "The showData Macro").

Beside the data section a tiddler may have all kind of other content.

Typically you will not access the data section text directly but use the methods given above. Nevertheless you may retrieve the text of the data section's content through the {{{DataTiddler.getDataText(tiddler)}}} function.


''Saving Changes''

The "setData" methods respect the "ForceMinorUpdate" and "AutoSave" configuration values. I.e. when "ForceMinorUpdate" is true changing a value using setData will not affect the "modifier" and "modified" attributes. With "AutoSave" set to true every setData will directly save the changes after a setData.


''Notifications''

No notifications are sent when a tiddler's data value is changed through the "setData" methods. 

''Escape Data Section''
In case that you want to use the text {{{<data>}}} or {{{</data>}}} in a tiddler text you must prefix the text with a tilde ('~'). Otherwise it may be wrongly considered as the data section. The tiddler text {{{~<data>}}} is displayed as {{{<data>}}}.


''The showData Macro''

By default the data of a tiddler (that is stored in the {{{<data>...</data>}}} section of the tiddler) is not displayed. If you want to display this data you may used the {{{<<showData ...>>}}} macro:

''Syntax:'' 
|>|{{{<<}}}''showData '' [''JSON''] [//tiddlerName//] {{{>>}}}|
|''JSON''|By default the data is rendered as a table with a "Name" and "Value" column. When defining ''JSON'' the data is rendered in JSON format|
|//tiddlerName//|Defines the tiddler holding the data to be displayed. When no tiddler is given the tiddler containing the showData macro is used. When the tiddler name contains spaces you must quote the name (or use the {{{[[...]]}}} syntax.)|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|


!Revision history
* v1.0.6 (2006-08-26) 
** Removed misleading comment
* v1.0.5 (2006-02-27) (Internal Release Only)
** Internal
*** Make "JSLint" conform
* v1.0.4 (2006-02-05)
** Bugfix: showData fails in TiddlyWiki 2.0
* v1.0.3 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.2 (2005-12-22)
** Enhancements:
*** Handle texts "<data>" or "</data>" more robust when used in a tiddler text or as a field value.
*** Improved (JSON) error messages.
** Bugs fixed: 
*** References are not updated when using the DataTiddler.
*** Changes to compound objects are not always saved.
*** "~</data>" is not rendered correctly (expected "</data>")
* v1.0.1 (2005-12-13)
** Features: 
*** The showData macro supports an optional "tiddlername" argument to specify the tiddler containing the data to be displayed
** Bugs fixed: 
*** A script immediately following a data section is deleted when the data is changed. (Thanks to GeoffS for reporting.)
* v1.0.0 (2005-12-12)
** initial version

!Code
***/
//{{{
//============================================================================
//============================================================================
// DataTiddlerPlugin
//============================================================================
//============================================================================

// Ensure that the DataTiddler Plugin is only installed once.
//
if (!version.extensions.DataTiddlerPlugin) {



version.extensions.DataTiddlerPlugin = {
 major: 1, minor: 0, revision: 6,
 date: new Date(2006, 7, 26), 
 type: 'plugin',
 source: "http://tiddlywiki.abego-software.de/#DataTiddlerPlugin"
};

// For backward compatibility with v1.2.x
//
if (!window.story) window.story=window; 
if (!TiddlyWiki.prototype.getTiddler) {
 TiddlyWiki.prototype.getTiddler = function(title) { 
 var t = this.tiddlers[title]; 
 return (t !== undefined && t instanceof Tiddler) ? t : null; 
 };
}

//============================================================================
// DataTiddler Class
//============================================================================

// ---------------------------------------------------------------------------
// Configurations and constants 
// ---------------------------------------------------------------------------

function DataTiddler() {
}

DataTiddler = {
 // Function to stringify a JavaScript value, producing the text for the data section content.
 // (Must match the implementation of DataTiddler.parse.)
 //
 stringify : null,
 

 // Function to parse the text for the data section content, producing a JavaScript value.
 // (Must match the implementation of DataTiddler.stringify.)
 //
 parse : null
};

// Ensure access for IE
window.DataTiddler = DataTiddler;

// ---------------------------------------------------------------------------
// Data Accessor and Mutator
// ---------------------------------------------------------------------------


// Returns the value of the given data field of the tiddler.
// When no such field is defined or its value is undefined
// the defaultValue is returned.
// 
// @param tiddler either a tiddler name or a tiddler
//
DataTiddler.getData = function(tiddler, field, defaultValue) {
 var t = (typeof tiddler == "string") ? store.getTiddler(tiddler) : tiddler;
 if (!(t instanceof Tiddler)) {
 throw "Tiddler expected. Got "+tiddler;
 }

 return DataTiddler.getTiddlerDataValue(t, field, defaultValue);
};


// Sets the value of the given data field of the tiddler to
// the value. When the value is equal to the defaultValue
// no value is set (and the field is removed)
//
// Changing data of a tiddler will not trigger notifications.
// 
// @param tiddler either a tiddler name or a tiddler
//
DataTiddler.setData = function(tiddler, field, value, defaultValue) {
 var t = (typeof tiddler == "string") ? store.getTiddler(tiddler) : tiddler;
 if (!(t instanceof Tiddler)) {
 throw "Tiddler expected. Got "+tiddler+ "("+t+")";
 }

 DataTiddler.setTiddlerDataValue(t, field, value, defaultValue);
};


// Returns the data object of the tiddler, with a property for every field.
//
// The properties of the returned data object may only be read and
// not be modified. To modify the data use DataTiddler.setData(...) 
// or the corresponding Tiddler method.
//
// If no data section is defined a new (empty) object is returned.
//
// @param tiddler either a tiddler name or a Tiddler
//
DataTiddler.getDataObject = function(tiddler) {
 var t = (typeof tiddler == "string") ? store.getTiddler(tiddler) : tiddler;
 if (!(t instanceof Tiddler)) {
 throw "Tiddler expected. Got "+tiddler;
 }

 return DataTiddler.getTiddlerDataObject(t);
};

// Returns the text of the content of the data section of the tiddler.
//
// When no data section is defined for the tiddler null is returned 
//
// @param tiddler either a tiddler name or a Tiddler
// @return [may be null]
//
DataTiddler.getDataText = function(tiddler) {
 var t = (typeof tiddler == "string") ? store.getTiddler(tiddler) : tiddler;
 if (!(t instanceof Tiddler)) {
 throw "Tiddler expected. Got "+tiddler;
 }

 return DataTiddler.readDataSectionText(t);
};


// ---------------------------------------------------------------------------
// Internal helper methods (must not be used by code from outside this plugin)
// ---------------------------------------------------------------------------

// Internal.
//
// The original JSONError is not very user friendly, 
// especially it does not define a toString() method
// Therefore we extend it here.
//
DataTiddler.extendJSONError = function(ex) {
 if (ex.name == 'JSONError') {
 ex.toString = function() {
 return ex.name + ": "+ex.message+" ("+ex.text+")";
 };
 }
 return ex;
};

// Internal.
//
// @param t a Tiddler
//
DataTiddler.getTiddlerDataObject = function(t) {
 if (t.dataObject === undefined) {
 var data = DataTiddler.readData(t);
 t.dataObject = (data) ? data : {};
 }
 
 return t.dataObject;
};


// Internal.
//
// @param tiddler a Tiddler
//
DataTiddler.getTiddlerDataValue = function(tiddler, field, defaultValue) {
 var value = DataTiddler.getTiddlerDataObject(tiddler)[field];
 return (value === undefined) ? defaultValue : value;
};


// Internal.
//
// @param tiddler a Tiddler
//
DataTiddler.setTiddlerDataValue = function(tiddler, field, value, defaultValue) {
 var data = DataTiddler.getTiddlerDataObject(tiddler);
 var oldValue = data[field];
 
 if (value == defaultValue) {
 if (oldValue !== undefined) {
 delete data[field];
 DataTiddler.save(tiddler);
 }
 return;
 }
 data[field] = value;
 DataTiddler.save(tiddler);
};

// Internal.
//
// Reads the data section from the tiddler's content and returns its text
// (as a String).
//
// Returns null when no data is defined.
//
// @param tiddler a Tiddler
// @return [may be null]
//
DataTiddler.readDataSectionText = function(tiddler) {
 var matches = DataTiddler.getDataTiddlerMatches(tiddler);
 if (matches === null || !matches[2]) {
 return null;
 }
 return matches[2];
};

// Internal.
//
// Reads the data section from the tiddler's content and returns it
// (as an internalized object).
//
// Returns null when no data is defined.
//
// @param tiddler a Tiddler
// @return [may be null]
//
DataTiddler.readData = function(tiddler) {
 var text = DataTiddler.readDataSectionText(tiddler);
 try {
 return text ? DataTiddler.parse(text) : null;
 } catch(ex) {
 throw DataTiddler.extendJSONError(ex);
 }
};

// Internal.
// 
// Returns the serialized text of the data of the given tiddler, as it
// should be stored in the data section.
//
// @param tiddler a Tiddler
//
DataTiddler.getDataTextOfTiddler = function(tiddler) {
 var data = DataTiddler.getTiddlerDataObject(tiddler);
 return DataTiddler.stringify(data);
};


// Internal.
// 
DataTiddler.indexOfNonEscapedText = function(s, subString, startIndex) {
 var index = s.indexOf(subString, startIndex);
 while ((index > 0) && (s[index-1] == '~')) { 
 index = s.indexOf(subString, index+1);
 }
 return index;
};

// Internal.
//
DataTiddler.getDataSectionInfo = function(text) {
 // Special care must be taken to handle "<data>" and "</data>" texts inside
 // a data section. 
 // Also take care not to use an escaped <data> (i.e. "~<data>") as the start 
 // of a data section. (Same for </data>)

 // NOTE: we are explicitly searching for a data section that contains a JSON
 // string, i.e. framed with braces. This way we are little bit more robust in
 // case the tiddler contains unescaped texts "<data>" or "</data>". This must
 // be changed when using a different stringifier.

 var startTagText = "<data>{";
 var endTagText = "}</data>";

 var startPos = 0;

 // Find the first not escaped "<data>".
 var startDataTagIndex = DataTiddler.indexOfNonEscapedText(text, startTagText, 0);
 if (startDataTagIndex < 0) {
 return null;
 }

 // Find the *last* not escaped "</data>".
 var endDataTagIndex = text.indexOf(endTagText, startDataTagIndex);
 if (endDataTagIndex < 0) {
 return null;
 }
 var nextEndDataTagIndex;
 while ((nextEndDataTagIndex = text.indexOf(endTagText, endDataTagIndex+1)) >= 0) {
 endDataTagIndex = nextEndDataTagIndex;
 }

 return {
 prefixEnd: startDataTagIndex, 
 dataStart: startDataTagIndex+(startTagText.length)-1, 
 dataEnd: endDataTagIndex, 
 suffixStart: endDataTagIndex+(endTagText.length)
 };
};

// Internal.
// 
// Returns the "matches" of a content of a DataTiddler on the
// "data" regular expression. Return null when no data is defined
// in the tiddler content.
//
// Group 1: text before data section (prefix)
// Group 2: content of data section
// Group 3: text behind data section (suffix)
//
// @param tiddler a Tiddler
// @return [may be null] null when the tiddler contains no data section, otherwise see above.
//
DataTiddler.getDataTiddlerMatches = function(tiddler) {
 var text = tiddler.text;
 var info = DataTiddler.getDataSectionInfo(text);
 if (!info) {
 return null;
 }

 var prefix = text.substr(0,info.prefixEnd);
 var data = text.substr(info.dataStart, info.dataEnd-info.dataStart+1);
 var suffix = text.substr(info.suffixStart);
 
 return [text, prefix, data, suffix];
};


// Internal.
//
// Saves the data in a <data> block of the given tiddler (as a minor change). 
//
// The "chkAutoSave" and "chkForceMinorUpdate" options are respected. 
// I.e. the TiddlyWiki *file* is only saved when AutoSave is on.
//
// Notifications are not send. 
//
// This method should only be called when the data really has changed. 
//
// @param tiddler
// the tiddler to be saved.
//
DataTiddler.save = function(tiddler) {

 var matches = DataTiddler.getDataTiddlerMatches(tiddler);

 var prefix;
 var suffix;
 if (matches === null) {
 prefix = tiddler.text;
 suffix = "";
 } else {
 prefix = matches[1];
 suffix = matches[3];
 }

 var dataText = DataTiddler.getDataTextOfTiddler(tiddler);
 var newText = 
 (dataText !== null) 
 ? prefix + "<data>" + dataText + "</data>" + suffix
 : prefix + suffix;
 if (newText != tiddler.text) {
 // make the change in the tiddlers text
 
 // ... see DataTiddler.MyTiddlerChangedFunction
 tiddler.isDataTiddlerChange = true;
 
 // ... do the action change
 tiddler.set(
 tiddler.title,
 newText,
 config.options.txtUserName, 
 config.options.chkForceMinorUpdate? undefined : new Date(),
 tiddler.tags);

 // ... see DataTiddler.MyTiddlerChangedFunction
 delete tiddler.isDataTiddlerChange;

 // Mark the store as dirty.
 store.dirty = true;
 
 // AutoSave if option is selected
 if(config.options.chkAutoSave) {
 saveChanges();
 }
 }
};

// Internal.
//
DataTiddler.MyTiddlerChangedFunction = function() {
 // Remove the data object from the tiddler when the tiddler is changed
 // by code other than DataTiddler code. 
 //
 // This is necessary since the data object is just a "cached version" 
 // of the data defined in the data section of the tiddler and the 
 // "external" change may have changed the content of the data section.
 // Thus we are not sure if the data object reflects the data section 
 // contents. 
 // 
 // By deleting the data object we ensure that the data object is 
 // reconstructed the next time it is needed, with the data defined by
 // the data section in the tiddler's text.
 
 // To indicate that a change is a "DataTiddler change" a temporary
 // property "isDataTiddlerChange" is added to the tiddler.
 if (this.dataObject && !this.isDataTiddlerChange) {
 delete this.dataObject;
 }
 
 // call the original code.
 DataTiddler.originalTiddlerChangedFunction.apply(this, arguments);
};


//============================================================================
// Formatters
//============================================================================

// This formatter ensures that "~<data>" is rendered as "<data>". This is used to 
// escape the "<data>" of a data section, just in case someone really wants to use
// "<data>" as a text in a tiddler and not start a data section.
//
// Same for </data>.
//
config.formatters.push( {
 name: "data-escape",
 match: "~<\\/?data>",

 handler: function(w) {
 w.outputText(w.output,w.matchStart + 1,w.nextMatch);
 }
} );


// This formatter ensures that <data>...</data> sections are not rendered.
//
config.formatters.push( {
 name: "data",
 match: "<data>",

 handler: function(w) {
 var info = DataTiddler.getDataSectionInfo(w.source);
 if (info && info.prefixEnd == w.matchStart) {
 w.nextMatch = info.suffixStart;
 } else {
 w.outputText(w.output,w.matchStart,w.nextMatch);
 }
 }
} );


//============================================================================
// Tiddler Class Extension
//============================================================================

// "Hijack" the changed method ---------------------------------------------------

DataTiddler.originalTiddlerChangedFunction = Tiddler.prototype.changed;
Tiddler.prototype.changed = DataTiddler.MyTiddlerChangedFunction;

// Define accessor methods -------------------------------------------------------

// Returns the value of the given data field of the tiddler. When no such field 
// is defined or its value is undefined the defaultValue is returned.
//
// When field is undefined (or null) the data object is returned. (See 
// DataTiddler.getDataObject.)
//
// @param field [may be null, undefined]
// @param defaultValue [may be null, undefined]
// @return [may be null, undefined]
//
Tiddler.prototype.data = function(field, defaultValue) {
 return (field) 
 ? DataTiddler.getTiddlerDataValue(this, field, defaultValue)
 : DataTiddler.getTiddlerDataObject(this);
};

// Sets the value of the given data field of the tiddler to the value. When the 
// value is equal to the defaultValue no value is set (and the field is removed).
//
// @param value [may be null, undefined]
// @param defaultValue [may be null, undefined]
//
Tiddler.prototype.setData = function(field, value, defaultValue) {
 DataTiddler.setTiddlerDataValue(this, field, value, defaultValue);
};


//============================================================================
// showData Macro
//============================================================================

config.macros.showData = {
 // Standard Properties
 label: "showData",
 prompt: "Display the values stored in the data section of the tiddler"
};

config.macros.showData.handler = function(place,macroName,params) {
 // --- Parsing ------------------------------------------

 var i = 0; // index running over the params
 // Parse the optional "JSON"
 var showInJSONFormat = false;
 if ((i < params.length) && params[i] == "JSON") {
 i++;
 showInJSONFormat = true;
 }
 
 var tiddlerName = story.findContainingTiddler(place).id.substr(7);
 if (i < params.length) {
 tiddlerName = params[i];
 i++;
 }

 // --- Processing ------------------------------------------
 try {
 if (showInJSONFormat) {
 this.renderDataInJSONFormat(place, tiddlerName);
 } else {
 this.renderDataAsTable(place, tiddlerName);
 }
 } catch (e) {
 this.createErrorElement(place, e);
 }
};

config.macros.showData.renderDataInJSONFormat = function(place,tiddlerName) {
 var text = DataTiddler.getDataText(tiddlerName);
 if (text) {
 createTiddlyElement(place,"pre",null,null,text);
 }
};

config.macros.showData.renderDataAsTable = function(place,tiddlerName) {
 var text = "|!Name|!Value|\n";
 var data = DataTiddler.getDataObject(tiddlerName);
 if (data) {
 for (var i in data) {
 var value = data[i];
 text += "|"+i+"|"+DataTiddler.stringify(value)+"|\n";
 }
 }
 
 wikify(text, place);
};


// Internal.
//
// Creates an element that holds an error message
// 
config.macros.showData.createErrorElement = function(place, exception) {
 var message = (exception.description) ? exception.description : exception.toString();
 return createTiddlyElement(place,"span",null,"showDataError","<<showData ...>>: "+message);
};

// ---------------------------------------------------------------------------
// Stylesheet Extensions (may be overridden by local StyleSheet)
// ---------------------------------------------------------------------------
//
setStylesheet(
 ".showDataError{color: #ffffff;background-color: #880000;}",
 "showData");


} // of "install only once"
// Used Globals (for JSLint) ==============

// ... TiddlyWiki Core
/*global createTiddlyElement, saveChanges, store, story, wikify */
// ... DataTiddler
/*global DataTiddler */
// ... JSON
/*global JSON */
 

/***
!JSON Code, used to serialize the data
***/
/*
Copyright (c) 2005 JSON.org

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The Software shall be used for Good, not Evil.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

/*
 The global object JSON contains two methods.

 JSON.stringify(value) takes a JavaScript value and produces a JSON text.
 The value must not be cyclical.

 JSON.parse(text) takes a JSON text and produces a JavaScript value. It will
 throw a 'JSONError' exception if there is an error.
*/
var JSON = {
 copyright: '(c)2005 JSON.org',
 license: 'http://www.crockford.com/JSON/license.html',
/*
 Stringify a JavaScript value, producing a JSON text.
*/
 stringify: function (v) {
 var a = [];

/*
 Emit a string.
*/
 function e(s) {
 a[a.length] = s;
 }

/*
 Convert a value.
*/
 function g(x) {
 var c, i, l, v;

 switch (typeof x) {
 case 'object':
 if (x) {
 if (x instanceof Array) {
 e('[');
 l = a.length;
 for (i = 0; i < x.length; i += 1) {
 v = x[i];
 if (typeof v != 'undefined' &&
 typeof v != 'function') {
 if (l < a.length) {
 e(',');
 }
 g(v);
 }
 }
 e(']');
 return;
 } else if (typeof x.toString != 'undefined') {
 e('{');
 l = a.length;
 for (i in x) {
 v = x[i];
 if (x.hasOwnProperty(i) &&
 typeof v != 'undefined' &&
 typeof v != 'function') {
 if (l < a.length) {
 e(',');
 }
 g(i);
 e(':');
 g(v);
 }
 }
 return e('}');
 }
 }
 e('null');
 return;
 case 'number':
 e(isFinite(x) ? +x : 'null');
 return;
 case 'string':
 l = x.length;
 e('"');
 for (i = 0; i < l; i += 1) {
 c = x.charAt(i);
 if (c >= ' ') {
 if (c == '\\' || c == '"') {
 e('\\');
 }
 e(c);
 } else {
 switch (c) {
 case '\b':
 e('\\b');
 break;
 case '\f':
 e('\\f');
 break;
 case '\n':
 e('\\n');
 break;
 case '\r':
 e('\\r');
 break;
 case '\t':
 e('\\t');
 break;
 default:
 c = c.charCodeAt();
 e('\\u00' + Math.floor(c / 16).toString(16) +
 (c % 16).toString(16));
 }
 }
 }
 e('"');
 return;
 case 'boolean':
 e(String(x));
 return;
 default:
 e('null');
 return;
 }
 }
 g(v);
 return a.join('');
 },
/*
 Parse a JSON text, producing a JavaScript value.
*/
 parse: function (text) {
 var p = /^\s*(([,:{}\[\]])|"(\\.|[^\x00-\x1f"\\])*"|-?\d+(\.\d*)?([eE][+-]?\d+)?|true|false|null)\s*/,
 token,
 operator;

 function error(m, t) {
 throw {
 name: 'JSONError',
 message: m,
 text: t || operator || token
 };
 }

 function next(b) {
 if (b && b != operator) {
 error("Expected '" + b + "'");
 }
 if (text) {
 var t = p.exec(text);
 if (t) {
 if (t[2]) {
 token = null;
 operator = t[2];
 } else {
 operator = null;
 try {
 token = eval(t[1]);
 } catch (e) {
 error("Bad token", t[1]);
 }
 }
 text = text.substring(t[0].length);
 } else {
 error("Unrecognized token", text);
 }
 } else {
 token = operator = undefined;
 }
 }


 function val() {
 var k, o;
 switch (operator) {
 case '{':
 next('{');
 o = {};
 if (operator != '}') {
 for (;;) {
 if (operator || typeof token != 'string') {
 error("Missing key");
 }
 k = token;
 next();
 next(':');
 o[k] = val();
 if (operator != ',') {
 break;
 }
 next(',');
 }
 }
 next('}');
 return o;
 case '[':
 next('[');
 o = [];
 if (operator != ']') {
 for (;;) {
 o.push(val());
 if (operator != ',') {
 break;
 }
 next(',');
 }
 }
 next(']');
 return o;
 default:
 if (operator !== null) {
 error("Missing value");
 }
 k = token;
 next();
 return k;
 }
 }
 next();
 return val();
 }
};

/***
!Setup the data serialization
***/

DataTiddler.format = "JSON";
DataTiddler.stringify = JSON.stringify;
DataTiddler.parse = JSON.parse;

//}}}

[[Summit Developer information]]
[[What's New]]
[[Introduction]]
[[We want your input!]]
''Dataset or volume'': the name of the drive. Examples:"V:" or "EXT:"
''Bin'': a directory
''Clip'': a movie
The sample code below shows how to delete an asset. An ~AssetExists call is added to first make sure that the source clip exists.

{{{
[C#]
// create a mediamgr object
IMediaMgr mediaMgr = iappServer.CreateMediaMgr(appName);

// if the clip does not exist, print error msg and return
if ( !mediaMgr.AssetExists("edl/cmf//local/V:/default/Clip") )
{
 Console.WriteLine("Clip V:/default/Clip doesn't exist!");
 return;
}

// delete clip 
mediaMgr.DeleteAsset("edl/cmf//local/V:/default/Clip");

// cleanup
mediaMgr.Dispose();
}}}
{{{
[C++]
// create a mediamgr object
IMediaMgrPtr spMediaMgr;
hr = spAppServer->CreateMediaMgr(_bstr_t("YourApplicationName"), 
 &spMediaMgr);

// if the clip does not exist, print error msg and return
if ( !spMediaMgr->AssetExists( _bstr_t("edl/cmf//local/V:/default/Clip") )
{
 printf("Clip V:/default/Clip doesn't exist!\n");
 return;
}

// copy clip
hr = spMediaMgr->DeleteAsset( _bstr_t("edl/cmf//local/V:/default/Clip") );
}}}
Developers unfamiliar with the K2 ~APIs should start with one of the two "Developer's Guide" PDF documents below. They provide a good overview of the ~APIs and examples of many common tasks.

[[K2 Protocol Developer's Guide|zip/K2%20Protocol%20Developer's%20Guide.pdf]] (ver: 1.1, updated: Feb 15 2008, size: 452 KB)
- Writing K2, ~MSeries, and Turbo applications with AMP, VDCP, and FTP ~APIs

[[K2 AppServer API Developer's Guide|zip/K2%20AppServer%20API%20Developer's%20Guide.pdf]] (ver: 1.0, updated: Feb 15 2008, size: 592 KB)
- Writing K2 applications with ~AppServer API
The following are answers to some questions that came up during the Developer Training Conference held in France in November 2007:

''AMP API:''
1. Do you have a sample socket connection example? 
Yes. Send an email to ~GVDeveloperSupport@thomson.net and we will provide it.

2. The Protocol Developer’s Guide is missing “\n” on the end of CRAT samples.
The updated version is [[available for download here.|Developer Guides]]

3. User Data Over 256 byes does not work when returning the data, yet it lets you set it.
Confirmed. A bug has been entered on this (CR 86631).

4. Cueing multiple clips apparently doesn’t work when using full paths.
Confirmed. A bug has been entered on this (CR 86634).

5. Does over record work with Long GOP?
Yes. What you are doing is creating a complex movie by recording. We seed the timecode generator again to match the timecode sent to ~ISimplePlayerRecorder::~CueRecord. Long GOP works because we aren’t really overwriting an existing file: we are creating a new one. Since all of our channels use two decoders, we can seamlessly switch from one file to another without regard to GOP, HD, or SD.

6. While playing a clip can the out point be extended?
Yes.

7. While playing with multiple clips cued is there a way to see exactly which clip is playing?
Yes, the ID Loaded Request command returns the currently playing clip when multiple clips have been cued.

8. Requested Auto Skip with Timecode.
Need to investigate.

9. Change Notices: User Data is currently very expensive because if user data changes, in order to find it via change notices, they have to repoll all the data for a clip, including the clip data and user data. This is due to the generic ~MOVIE_MODIFIED change notice that AMP returns. It would be far easier if AMP returned a notice for this.
Need to investigate.

10. Can we set user data before a record starts?
Need to investigate.
 
11. Can we do MPEG with AMP in 3.2?
Yes. From the K2 Media Client System Guide, page 175 of
http://www.grassvalley.com/docs/Manuals/servers/k2/071-8460-03.pdf

|Streaming file format|Video elementary format|Audio elementary format|File based Import/Export|FTP stream Import/Export|Other information|
|MPEG|MPEG-2|48kHz MPEG-1 (layer 1 & 2) SMPTE 320M AES3 LPCM AC-3 DVD/VOB LPCM DVD/VOB AC-3|Yes No|Yes No|Supports import of MPEG-2 program and transport streams. If the transport stream contains multiple programs, the first detected program in the transport stream is imported as a K2 clip.|

12. Does the erase segment clear the busy bit before the Erase Unused Media call actually finishes? In other words, is the call conducted in the background? How long does the call take if it is blocking.
Need to investigate.

13. How do we determine if a clip is loaded on another K2 SAN client?
Currently, we do not have this ability in our architecture.
 
14. During Preset to Preview timeline transition, how many frames does it take to status bits, and the correct timecode?
Need to investigate.

15. Can you record off system clock with Time of Day set to system clock?
Need to investigate.

16. Are we going to add Audio Tagging support for AMP?
Yes, we are currently working on a command specification for these new commands.

17. Can we cue in & out points in one transaction? 
Yes, the new ~AppendPreset command supports this in 3.2.

18. Exactly what form of AVI file does it support?
From the K2 Media Client System Guide, page 175 of
http://www.grassvalley.com/docs/Manuals/servers/k2/071-8460-03.pdf

|Streaming file format|Video elementary format|Audio elementary format|File based Import/Export|FTP stream Import/Export|Other information|
|AVI|DV25 DV50 DVCAM|48 kHz, 16 bit, or 24 bit PCM|Yes Yes|No No|Type-2 (non-interleaved) DV video only. Audio tracks handled as stereo pairs|
 
''BVW API:''
19. Please provide a list of supported commands for BVW.
A list of supported BVW commands can be found here:
[[BVW commands and responses]]

20. What is the device ID response for the K2? This is important so a controller can know whether or not the extended BVW commands are supported. 
Extended BVW command support was recently added. If the user issues the “Device Type Request” command (standard BVW command, 00.11) the BVW protocol on the K2 will reply that it is ~BVW-75 machine (20.25 or 21.25 depending on whether it’s NTSC or PAL, respectively). We also support an extended BVW command (01.11) which will reply with D8.xx indicating that it is actually a disk recorder.

21. Is the working directory/bin now changed in 3.2 when you load a clip from a bin that is not in the current working directory/bin?
Yes.

''VDCP API:''
22. Do we support over record and over record w/ timecode? 
Need to investigate.

23. Is record with data supported? 
Yes.

24. Is user data supported? 
No.

''~AppServer API:''
25. Why did we choose .Net for the ~AppServer API?
Our previous server products used Microsoft COM servers so we wanted to keep using the libraries and functionality we already had. We chose to wrap our COM servers with a .NET interface because it provided a good way to remotely control the K2 and allowed us to move forward with newer languages and tools. We have successfully controlled the K2 using Unmanaged C++, Managed C#, and Python. Also, Mono could likely be used for developing applications that 
control the K2 from a Linux platform.

26. How long can a suite name be?
The suite name is implemented by a System.String object in the .NET framework. We do not restrict the length of the suite name, but we recommend that you constrain the length to a maximum of 256 characters.

27. Is there an error code to error message call/function?
No, there is not at this time. A list of error codes and error code names can be found here:
[[Grass Valley K2 error codes]]

28. Insert Edits: can you change it once it’s in place? (Can you edit an edit?)
Yes. You can adjust many properties of an edit afterwards (mark in, mark out, audio gains, etc). Currently, you cannot insert an edit across an edit boundary. Soon you will be able to consolidate edits so that you can insert an edit anywhere. The result is a complex movie with references to other movies.

29. Insert Edits: can you pass in timecode rather than fields?
Yes.

30. How can you get a notification of a context change? Switching Main to Preview timelines.
Need to document an example.

31. Are we going to provide an installer for API ~DLLs?
Need to investigate.

32. What is and isn’t restricted by the username, password, domain login info.
Need to document.

33. How do I get current position in playlist and what clip I’m on?
Need to document an example.

34. Developers want better API documentation (not just examples). 
Now that we’ve moved to Visual Studio 2005 we’re investigating Microsoft’s Sandcastle documenting tool as an alternative to ~NDoc. We need to improve our inline XML commenting as well.

''FTP:''
35. Can you issue SIZE while transferring?
SIZE returns the byte size of a clip on the K2, NOT the size of the stream. So, yes, you can issue a SIZE command while transferring. You want to use it sparsely, though, because the K2 will query file sizes, do database activity, and then compute the number. These are resource expensive operations so you want to limit these activites.

36. When you log out during a transfer, does it continue to transfer?
No, it aborts the transfer.

37. Can you provide documentation on exactly what we can and cannot import/export?
Yes, see the K2 Media Client System Guide, page 175 of
http://www.grassvalley.com/docs/Manuals/servers/k2/071-8460-03.pdf

''MXF:''
38. Do we preserve metadata inbound on MXF’s? 
MXF required tags, yes.

39. Do we preserve clip user data embedded in the MXF? 
No.

40. Would it make it into the user data on the K2? 
Not yet.

41. Do we preserve dark metadata?
Not yet.

''Logging:''
42. What does “Insert Separator” menu item do in ~LogViewer?
Currently, nothing. ~LogViewer is an engineering tool. This menu item was added to be able to add a separator line in the log, but the functionality has not been implemented. Similar functionality can be achieved using the ~LogInjector application. ~LogInjector lets you add any of your own messages to the K2’s log.

43. Is there any way to sync the K2 or Log to LTC?
No.

''Ancillary Data and CC:''
44. Provide spec for new commands.
Details available upon request.

''Emulator:''
45. Where can more information about the K2 Emulator be found?
The link below provides information about how to download and install the K2 Emulator. It also explains the functionality and restrictions of the current version.
[[K2 Emulator]]

46. What are the emulator PC requirements?
The PC requirements are the same as the ~ControlPoint requirements found here:
[[ControlPoint PC System Requirements]]

47. Does emulator support serial control? 
The emulator currently supports ~AppServer and AMP ~APIs over Ethernet. We will begin testing with a ~RS422 card soon and see if we can support AMP, BVW, and VDCP over serial connections.

48. Do you provide test clips? 
Yes. Can find them here: [[Test Clips]]

''Procedural Suggestions:''
49. Can you post developer release notes for developer related fixes and notices?
This is coming. We have started tracking changes. In the future we plan to roll out a new web site for Development Documentation which also contains a page dedicated to such changes.

50. Can we publish database schema?
Need to investigate.

''Other:''
51. Can Sabretooth licensing be made available for developers to use for their applications?
Need to investigate.

Instead of using a hardcoded "V:" for the K2's volume name use ~MediaMgr's ~EnumerateVolumes() function to programatically determine the volume name. See this example on [[How to query for volume or drive letter]].

The reasons you don't want to hardcode the drive letter are:

1. The K2 and future products may support additional drives. By enumerating the volumes you will get all volume names supported by the server.

2. The K2 Emulator uses a "C:" volume name instead of "V:". By using the ~EnumerateVolumes function instead your application will be able to connect to both the K2 and the K2 Emulator without needing changes to your code.

Here is an example of the XML results returned from ~MediaMgr's ~EnumerateVolumes function. The volume name can be found in the Dataset element near the bottom:
{{{
<Select Query="" StartIndex="">
 <Header version="1" selectionNamespace="x-schema:#Schema1"/>
 <Schema name="Schema1" xmlns="urn:schemas-microsoft-com:xml-data" xmlns:dt="urn:schemas-microsoft-com:datatypes">
 <AttributeType name="MachineName" dt:type="string"/>
 <AttributeType name="DatasetName" dt:type="string"/>
 <AttributeType name="TotalSpace" dt:type="i4"/>
 <AttributeType name="FreeSpace" dt:type="i4"/>
 <AttributeType name="UsedSpace" dt:type="i4"/>
 <ElementType name="Dataset">
 <attribute type="MachineName"/>
 <attribute type="DatasetName"/>
 <attribute type="TotalSpace"/>
 <attribute type="FreeSpace"/>
 <attribute type="UsedSpace"/>
 </ElementType>
 <ElementType name="Data">
 <element type="Dataset"/>
 </ElementType>
 </Schema>
 <ns:Data xmlns:ns="x-schema:#Schema1">
 <ns:Dataset MachineName="K2-1" DatasetName="V:" TotalSpace="438272" FreeSpace="438272" UsedSpace="0"/>
 </ns:Data>
</Select>
}}}
Starting in K2 version 3.2 software any client using the ~AppServer API can enable function trace logging. This lets you capture a time-stamped log of a client’s ~AppService calls, the parameters being sent, and the data returned from the call.

''@@color(blue):Note:@@ this is for capturing ~AppSerivce calls coming __from__ a client. So, as such, you need to configure ~NLog.config file in your client's application directory.''

''Uses:''
For starters, you can enable function trace logging and then use ~AppCenter to perform a task. You can then review the function trace log and see what calls and parameters were used to perform each operation. This is a great tool to help learn the calls and parameters of the ~AppServer API.

Later, in your development cycle, you can enable function trace logging for your application. This helps you to look at your application like a black box and see the calls and the timing of calls that you are making. Especially in a multi-threaded application, this is helpful for verifying:
- If calls are being made to frequently?
- If redundant calls are being made?
- If parameters are wrong or missing?

''@@color(red):Warning:@@ make sure that function trace logging is disabled in live environments''

Function trace logging is a good tool for evaluating performance, but you should disable it in a live environment. The additional logging calls add to file I/O overhead and could affect performance.

On the K2 Client or Control Point, in the c:\profile directory, you will see three ~NLog.* files. The ~AppService proxy objects use ~NLog to log all calls and parameters as well as the returned results. ~NLog offers great flexibility to turn on-or-off what gets logged to where based on logging targets and rules. This is configured in the ~NLog.config file. You can learn more about ~NLog and it’s configuration at http://www.nlog-project.org/.

For example, starting up ~AppCenter with NLog logging enabled produces a log that looks like this:

{{{
08-31-2007 15:34:26.865 [Workstation:3852] GrassValley.Mseries.AppServer.AppServerMgrProxy..ctor 
08-31-2007 15:34:27.256 [Workstation:3852] GrassValley.Mseries.AppServer.AppServerMgrProxy.SetHost ("localhost")
08-31-2007 15:34:27.490 [Workstation:3852] GrassValley.Mseries.AppServer.AppServerMgrProxy.SetUserCredentials ("Username", "PASSWORD", "", False)
08-31-2007 15:34:27.552 [Workstation:3852] GrassValley.Mseries.AppServer.AppServerMgrProxy.GetEncryptedUserCredentials 
08-31-2007 15:34:27.662 [Workstation:3852] GrassValley.Mseries.AppServer.AppServerMgrProxy.Connect 
08-31-2007 15:34:28.459 [Workstation:3852] GrassValley.Mseries.AppServer.AppServerMgrProxy.Connect returning: true
08-31-2007 15:34:28.459 [Workstation:3852] GrassValley.Mseries.AppServer.AppServerMgrProxy.IsUserAuthenticated 
08-31-2007 15:34:28.568 [Workstation:3852] GrassValley.Mseries.AppServer.AppServerMgrProxy.IsUserAuthenticated returning: True
08-31-2007 15:34:28.568 [Workstation:3852] GrassValley.Mseries.AppServer.AppServerMgrProxy.IsUserInRole ("BUILTIN\Administrators")
08-31-2007 15:34:28.662 [Workstation:3852] GrassValley.Mseries.AppServer.AppServerMgrProxy.IsUserInRole returning: True
08-31-2007 15:34:28.677 [Workstation:3852] GrassValley.Mseries.AppServer.AppServerMgrProxy.GetSystemStatus 
08-31-2007 15:34:28.677 [Workstation:3852] GrassValley.Mseries.AppServer.AppServerMgrProxy.GetSystemStatus returning: ("Normal", UP_AND_RUNNING, NORMAL)
08-31-2007 15:34:30.099 [Workstation:3852] GrassValley.Mseries.AppServer.AppServerMgrProxy.CreateAppServer ("K2-1_localConnection", "Workstation")
etc.
}}}

As you can see, information about every process, process id, class, function, parameters, and results gets logged. This way it is very easy to see the exact commands and parameters to use to perform the operations you want.

''To enable this function trace logging:''
1. In Explorer go to c:\profile and look for the "~NLog.config" file. This is an XML file which specifies the logging target and the rules for logging.

2. Right-click on it and disable the "read only" checkbox.

3a. You may either, replace the ~NLog.config file with the [[one found here|zip/NLog.config]], or
3b. Open ~NLog.config in a text editor. You'll see that all targets and rules are commented out with XML comments. Good choices to uncomment are the first target with the name "dailylogfile" and the last logger in the rules section which writes most messages, but avoid ~AppCenter update chatter. Note that the first target writes log entries to the file "C:\logs\~AppService_calls.log". This file rotates daily, keeping 2 backup files.

4. Start your application perform the functionality that want, then view all calls in the C:\logs\~AppService_calls.log file.

''Note:''
The function trace log captures ~AppServer API calls only. Other protocol calls and mouse and keyboard commands are not captured here.
This section lists resources and sample source code for performaning many common K2 tasks.

''Summit Features:''
* [[Live Play]] - How to play a clip in "live" mode (within a 0.5 second of the record position)
* ''[[Flying Mix Effects]]'' - How to create flying mix effects between foreground and background contexts of a channel.
* ''[[Playlist Mix Effects]]'' - How to create a playlist with section and event mix effects transitions.

''Resources:''
* [[Sample Projects]] - Simple K2 client applications written in C# and C++.
* [[C# Sample Scripts|CS-Script Sample Scripts]] - many C# samples that run from a single file.
* [[Install Client Libraries]] - Libraries you will need for writing K2 ~AppService clients.
* [[Include Files]] - Files to include in your K2 ~AppService clients.
* [[Grass Valley K2 error codes]] - a list of K2 error code numbers and names.
* [[Enabling Function Trace Logging|How to turn on function logging to see the calls AppCenter is making]] - how to see all calls being made to ~AppService.

''Connection and System Status:''
* [[Basic Connection]] - How to connect to a K2 ~AppService.
* [[Create an AppServer]] - How to create an ~AppServer.
* [[Get system status]] - How to get K2's system status.
* [[Get K2 software version]] - How to get the version info of the software running on the K2.
* [[Get configuration information]] - How to get the K2 Client's configuration information.
* [[Get channel owner]] - How to get info about who owns a channel.
* [[Get channel properties|Get channel status properties]] - How to get channel status properties.
* [[Log a Message]] - How to log messages.
* [[Read log messages]] - How to read log messages.

''Timeline and Transport Control:''
* [[Play a clip]] - How to create a controller and play a clip.
* [[Record a 5 second clip]] - How to create a controller and record a 5 second clip.
* [[Create a basic playlist]] - How to create and play a simple playlist.
* [[Channel Ganging|Getting and setting Channel Ganging properties]] - How to configure ganging.

''Clip Enumeration:''
* [[Get a list of clips]] - How to enumerate assets in a bin and get their asset properties.
* [[Getting event notifications]] - How to get notifications of changes (assets, bins, edits, etc.)

''Asset Management:''
* [[Create a Bin]] - How to create a bin.
* [[Copy a Clip]] - How to copy a clip.
* [[Delete a Clip]] - How to delete a clip.
* [[Transfer a File]] - How to transfer a file.
* [[Import/export different file types|Transfer clips: importing and exporting various file types]] - how to import and export GXF, MXF, MOV, and AVI files.
* [[Get asset properties|Get asset properties]] - How to get asset properties.
* [[Import and delete tracks from a clip]] - How to import, delete, move, and label tracks in a clip.
* [[Get asset thumbnail image]] - How to get a thumbnail image for an asset and save it.
see [[Troubleshooting and gathering useful logs]]
On a Summit Client you have the ability to create on-the-fly mix effects between foreground and background channel context changes. Available on-the-fly transition are:
* video dissolve between contexts
* video fade-thru-matte color between contexts
* audio cross-fade between contexts
* audio fade-thru-silence between contexts

''@@color(red):Note:@@'' the Summit must have an ~AppCenter Pro license for this feature to work.

Full sample code can be downloaded from the [[CS-Script Sample Scripts]] section. Look for the sample named [[FlyingMixEffects.cs|CS-Script/FlyingMixEffects.cs]].

Below is a code snippet showing how to configure a dissolve between contexts. Also refer to the [[Context changes]] section for more information about contexts and triggers:
{{{
public enum MatteColor
{
	Black   = 0x108080,
	White   = 0xEB8080,
	Yellow  = 0xD68A10,
	Cyan    = 0xBC1099,
	Green   = 0xAC1A29,
	Magenta = 0x4EE5D6,
	Red     = 0x3EF066,
	Blue    = 0x2075F0
};

public enum VideoTransition
{
	None 		= 0,	// standard video cut
	Dissolve 	= 1,	// dissolve between 2 video tracks (available with both flying and playlist mix effects)
	FadeFromMatte 	= 2,	// fades from matte color to video (available with playlist section mix effects only)
	FadeToMatte 	= 3,	// fades from video to matte color (available with playlist section mix effects only)
	FadeThruMatte 	= 4,	// fades from video to matte color to video (available with both flying and playlist mix effects)
};

public enum AudioTransition 
{
	None 		= 0,	// standard audio cut
	CrossFade 	= 1,	// cross-fades between to audio tracks (available with both flying and playlist mix effects)
	FadeUp 		= 2,	// audio fade up (available with playlist section mix effects only)
	FadeDown 	= 3,	// audio fade down (available with playlist section mix effects only)
	FadeThruSilence = 4,	// fade from audio to silence to audio (available with both flying and playlist mix effects)
};

string channel = "C1";

// create a controller, player, and preview
bool isNewController = false;
ISimpleController controller = iappServer.CreateController(
	appName, channel, out isNewController);
ISimplePlayerRecorder player = (ISimplePlayerRecorder) controller;
ITwoHeadPlayerRecorder preview = (ITwoHeadPlayerRecorder) controller;


// load player, cue to start and start playing it
player.Load("edl/cmf//local/V:/default/Clip_1");
player.CueStart();
player.Play();

// load another clip on the background preview context
preview.LoadPreview("edl/cmf//local/V:/default/Clip_2");

// preview.ActivateMode = "replace"; // when context switching occurs, inactive context is ejected
preview.ActivateMode = "swap"; 	// when context switching occurs, clip remains on inactive context


// EXAMPLE 1: immediate dissolve

// wait a few seconds... just so we can see the
// transition from foreground to background context
Thread.Sleep(3000);

// cue the preview context and start it playing in the
// background. Note: that the "manual" context change
// performsthe cueing & starts playing, but it does not
// switch the context from foreground to background
preview.BeginPreviewContextChange ();
preview.CueStart();
preview.Play();
preview.CommitContextChange("manual");

// setup a video dissolve & audio crossfade for 2 seconds
Object[] transParams = new Object[4];
transParams[0] = 120;		// fields of transition
transParams[1] = (int) VideoTransition.Dissolve;	// video dissolve
transParams[2] = (int) AudioTransition.CrossFade;	// audio crossfade
transParams[3] = (int) MatteColor.Black;			// black matte


// now switch the context and do  the transition.
// note that the "transition" context changes performs
// the "commit" so no CommitContextChange is needed
// afterwards.					
preview.BeginPreviewContextChange();
preview.ScheduleContextChange ("transition", transParams );


// wait some time just to see the transition and the background clip
Thread.Sleep(3000);
}}}
/***
|''Name:''|ForEachTiddlerPlugin|
|''Version:''|1.0.8 (2007-04-12)|
|''Source:''|http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]|
|''Copyright:''|&copy; 2005-2007 [[abego Software|http://www.abego-software.de]]|
|''TiddlyWiki:''|1.2.38+, 2.0|
|''Browser:''|Firefox 1.0.4+; Firefox 1.5; InternetExplorer 6.0|
!Description

Create customizable lists, tables etc. for your selections of tiddlers. Specify the tiddlers to include and their order through a powerful language.

''Syntax:'' 
|>|{{{<<}}}''forEachTiddler'' [''in'' //tiddlyWikiPath//] [''where'' //whereCondition//] [''sortBy'' //sortExpression// [''ascending'' //or// ''descending'']] [''script'' //scriptText//] [//action// [//actionParameters//]]{{{>>}}}|
|//tiddlyWikiPath//|The filepath to the TiddlyWiki the macro should work on. When missing the current TiddlyWiki is used.|
|//whereCondition//|(quoted) JavaScript boolean expression. May refer to the build-in variables {{{tiddler}}} and  {{{context}}}.|
|//sortExpression//|(quoted) JavaScript expression returning "comparable" objects (using '{{{<}}}','{{{>}}}','{{{==}}}'. May refer to the build-in variables {{{tiddler}}} and  {{{context}}}.|
|//scriptText//|(quoted) JavaScript text. Typically defines JavaScript functions that are called by the various JavaScript expressions (whereClause, sortClause, action arguments,...)|
|//action//|The action that should be performed on every selected tiddler, in the given order. By default the actions [[addToList|AddToListAction]] and [[write|WriteAction]] are supported. When no action is specified [[addToList|AddToListAction]]  is used.|
|//actionParameters//|(action specific) parameters the action may refer while processing the tiddlers (see action descriptions for details). <<tiddler [[JavaScript in actionParameters]]>>|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|

See details see [[ForEachTiddlerMacro]] and [[ForEachTiddlerExamples]].

!Revision history
* v1.0.8 (2007-04-12)
** Adapted to latest TiddlyWiki 2.2 Beta importTiddlyWiki API (introduced with changeset 2004). TiddlyWiki 2.2 Beta builds prior to changeset 2004 are no longer supported (but TiddlyWiki 2.1 and earlier, of cause)
* v1.0.7 (2007-03-28)
** Also support "pre" formatted TiddlyWikis (introduced with TW 2.2) (when using "in" clause to work on external tiddlers)
* v1.0.6 (2006-09-16)
** Context provides "viewerTiddler", i.e. the tiddler used to view the macro. Most times this is equal to the "inTiddler", but when using the "tiddler" macro both may be different.
** Support "begin", "end" and "none" expressions in "write" action
* v1.0.5 (2006-02-05)
** Pass tiddler containing the macro with wikify, context object also holds reference to tiddler containing the macro ("inTiddler"). Thanks to SimonBaird.
** Support Firefox 1.5.0.1
** Internal
*** Make "JSLint" conform
*** "Only install once"
* v1.0.4 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.3 (2005-12-22)
** Features: 
*** Write output to a file supports multi-byte environments (Thanks to Bram Chen) 
*** Provide API to access the forEachTiddler functionality directly through JavaScript (see getTiddlers and performMacro)
** Enhancements:
*** Improved error messages on InternetExplorer.
* v1.0.2 (2005-12-10)
** Features: 
*** context object also holds reference to store (TiddlyWiki)
** Fixed Bugs: 
*** ForEachTiddler 1.0.1 has broken support on win32 Opera 8.51 (Thanks to BrunoSabin for reporting)
* v1.0.1 (2005-12-08)
** Features: 
*** Access tiddlers stored in separated TiddlyWikis through the "in" option. I.e. you are no longer limited to only work on the "current TiddlyWiki".
*** Write output to an external file using the "toFile" option of the "write" action. With this option you may write your customized tiddler exports.
*** Use the "script" section to define "helper" JavaScript functions etc. to be used in the various JavaScript expressions (whereClause, sortClause, action arguments,...).
*** Access and store context information for the current forEachTiddler invocation (through the build-in "context" object) .
*** Improved script evaluation (for where/sort clause and write scripts).
* v1.0.0 (2005-11-20)
** initial version

!Code
***/
//{{{

	
//============================================================================
//============================================================================
//		   ForEachTiddlerPlugin
//============================================================================
//============================================================================

// Only install once
if (!version.extensions.ForEachTiddlerPlugin) {

if (!window.abego) window.abego = {};

version.extensions.ForEachTiddlerPlugin = {
	major: 1, minor: 0, revision: 8, 
	date: new Date(2007,3,12), 
	source: "http://tiddlywiki.abego-software.de/#ForEachTiddlerPlugin",
	licence: "[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]",
	copyright: "Copyright (c) abego Software GmbH, 2005-2007 (www.abego-software.de)"
};

// For backward compatibility with TW 1.2.x
//
if (!TiddlyWiki.prototype.forEachTiddler) {
	TiddlyWiki.prototype.forEachTiddler = function(callback) {
		for(var t in this.tiddlers) {
			callback.call(this,t,this.tiddlers[t]);
		}
	};
}

//============================================================================
// forEachTiddler Macro
//============================================================================

version.extensions.forEachTiddler = {
	major: 1, minor: 0, revision: 8, date: new Date(2007,3,12), provider: "http://tiddlywiki.abego-software.de"};

// ---------------------------------------------------------------------------
// Configurations and constants 
// ---------------------------------------------------------------------------

config.macros.forEachTiddler = {
	 // Standard Properties
	 label: "forEachTiddler",
	 prompt: "Perform actions on a (sorted) selection of tiddlers",

	 // actions
	 actions: {
		 addToList: {},
		 write: {}
	 }
};

// ---------------------------------------------------------------------------
//  The forEachTiddler Macro Handler 
// ---------------------------------------------------------------------------

config.macros.forEachTiddler.getContainingTiddler = function(e) {
	while(e && !hasClass(e,"tiddler"))
		e = e.parentNode;
	var title = e ? e.getAttribute("tiddler") : null; 
	return title ? store.getTiddler(title) : null;
};

config.macros.forEachTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
	// config.macros.forEachTiddler.traceMacroCall(place,macroName,params,wikifier,paramString,tiddler);

	if (!tiddler) tiddler = config.macros.forEachTiddler.getContainingTiddler(place);
	// --- Parsing ------------------------------------------

	var i = 0; // index running over the params
	// Parse the "in" clause
	var tiddlyWikiPath = undefined;
	if ((i < params.length) && params[i] == "in") {
		i++;
		if (i >= params.length) {
			this.handleError(place, "TiddlyWiki path expected behind 'in'.");
			return;
		}
		tiddlyWikiPath = this.paramEncode((i < params.length) ? params[i] : "");
		i++;
	}

	// Parse the where clause
	var whereClause ="true";
	if ((i < params.length) && params[i] == "where") {
		i++;
		whereClause = this.paramEncode((i < params.length) ? params[i] : "");
		i++;
	}

	// Parse the sort stuff
	var sortClause = null;
	var sortAscending = true; 
	if ((i < params.length) && params[i] == "sortBy") {
		i++;
		if (i >= params.length) {
			this.handleError(place, "sortClause missing behind 'sortBy'.");
			return;
		}
		sortClause = this.paramEncode(params[i]);
		i++;

		if ((i < params.length) && (params[i] == "ascending" || params[i] == "descending")) {
			 sortAscending = params[i] == "ascending";
			 i++;
		}
	}

	// Parse the script
	var scriptText = null;
	if ((i < params.length) && params[i] == "script") {
		i++;
		scriptText = this.paramEncode((i < params.length) ? params[i] : "");
		i++;
	}

	// Parse the action. 
	// When we are already at the end use the default action
	var actionName = "addToList";
	if (i < params.length) {
	   if (!config.macros.forEachTiddler.actions[params[i]]) {
			this.handleError(place, "Unknown action '"+params[i]+"'.");
			return;
		} else {
			actionName = params[i]; 
			i++;
		}
	} 
	
	// Get the action parameter
	// (the parsing is done inside the individual action implementation.)
	var actionParameter = params.slice(i);


	// --- Processing ------------------------------------------
	try {
		this.performMacro({
				place: place, 
				inTiddler: tiddler,
				whereClause: whereClause, 
				sortClause: sortClause, 
				sortAscending: sortAscending, 
				actionName: actionName, 
				actionParameter: actionParameter, 
				scriptText: scriptText, 
				tiddlyWikiPath: tiddlyWikiPath});

	} catch (e) {
		this.handleError(place, e);
	}
};

// Returns an object with properties "tiddlers" and "context".
// tiddlers holds the (sorted) tiddlers selected by the parameter,
// context the context of the execution of the macro.
//
// The action is not yet performed.
//
// @parameter see performMacro
//
config.macros.forEachTiddler.getTiddlersAndContext = function(parameter) {

	var context = config.macros.forEachTiddler.createContext(parameter.place, parameter.whereClause, parameter.sortClause, parameter.sortAscending, parameter.actionName, parameter.actionParameter, parameter.scriptText, parameter.tiddlyWikiPath, parameter.inTiddler);

	var tiddlyWiki = parameter.tiddlyWikiPath ? this.loadTiddlyWiki(parameter.tiddlyWikiPath) : store;
	context["tiddlyWiki"] = tiddlyWiki;
	
	// Get the tiddlers, as defined by the whereClause
	var tiddlers = this.findTiddlers(parameter.whereClause, context, tiddlyWiki);
	context["tiddlers"] = tiddlers;

	// Sort the tiddlers, when sorting is required.
	if (parameter.sortClause) {
		this.sortTiddlers(tiddlers, parameter.sortClause, parameter.sortAscending, context);
	}

	return {tiddlers: tiddlers, context: context};
};

// Returns the (sorted) tiddlers selected by the parameter.
//
// The action is not yet performed.
//
// @parameter see performMacro
//
config.macros.forEachTiddler.getTiddlers = function(parameter) {
	return this.getTiddlersAndContext(parameter).tiddlers;
};

// Performs the macros with the given parameter.
//
// @param parameter holds the parameter of the macro as separate properties.
//				  The following properties are supported:
//
//						place
//						whereClause
//						sortClause
//						sortAscending
//						actionName
//						actionParameter
//						scriptText
//						tiddlyWikiPath
//
//					All properties are optional. 
//					For most actions the place property must be defined.
//
config.macros.forEachTiddler.performMacro = function(parameter) {
	var tiddlersAndContext = this.getTiddlersAndContext(parameter);

	// Perform the action
	var actionName = parameter.actionName ? parameter.actionName : "addToList";
	var action = config.macros.forEachTiddler.actions[actionName];
	if (!action) {
		this.handleError(parameter.place, "Unknown action '"+actionName+"'.");
		return;
	}

	var actionHandler = action.handler;
	actionHandler(parameter.place, tiddlersAndContext.tiddlers, parameter.actionParameter, tiddlersAndContext.context);
};

// ---------------------------------------------------------------------------
//  The actions 
// ---------------------------------------------------------------------------

// Internal.
//
// --- The addToList Action -----------------------------------------------
//
config.macros.forEachTiddler.actions.addToList.handler = function(place, tiddlers, parameter, context) {
	// Parse the parameter
	var p = 0;

	// Check for extra parameters
	if (parameter.length > p) {
		config.macros.forEachTiddler.createExtraParameterErrorElement(place, "addToList", parameter, p);
		return;
	}

	// Perform the action.
	var list = document.createElement("ul");
	place.appendChild(list);
	for (var i = 0; i < tiddlers.length; i++) {
		var tiddler = tiddlers[i];
		var listItem = document.createElement("li");
		list.appendChild(listItem);
		createTiddlyLink(listItem, tiddler.title, true);
	}
};

abego.parseNamedParameter = function(name, parameter, i) {
	var beginExpression = null;
	if ((i < parameter.length) && parameter[i] == name) {
		i++;
		if (i >= parameter.length) {
			throw "Missing text behind '%0'".format([name]);
		}
		
		return config.macros.forEachTiddler.paramEncode(parameter[i]);
	}
	return null;
}

// Internal.
//
// --- The write Action ---------------------------------------------------
//
config.macros.forEachTiddler.actions.write.handler = function(place, tiddlers, parameter, context) {
	// Parse the parameter
	var p = 0;
	if (p >= parameter.length) {
		this.handleError(place, "Missing expression behind 'write'.");
		return;
	}

	var textExpression = config.macros.forEachTiddler.paramEncode(parameter[p]);
	p++;

	// Parse the "begin" option
	var beginExpression = abego.parseNamedParameter("begin", parameter, p);
	if (beginExpression !== null) 
		p += 2;
	var endExpression = abego.parseNamedParameter("end", parameter, p);
	if (endExpression !== null) 
		p += 2;
	var noneExpression = abego.parseNamedParameter("none", parameter, p);
	if (noneExpression !== null) 
		p += 2;

	// Parse the "toFile" option
	var filename = null;
	var lineSeparator = undefined;
	if ((p < parameter.length) && parameter[p] == "toFile") {
		p++;
		if (p >= parameter.length) {
			this.handleError(place, "Filename expected behind 'toFile' of 'write' action.");
			return;
		}
		
		filename = config.macros.forEachTiddler.getLocalPath(config.macros.forEachTiddler.paramEncode(parameter[p]));
		p++;
		if ((p < parameter.length) && parameter[p] == "withLineSeparator") {
			p++;
			if (p >= parameter.length) {
				this.handleError(place, "Line separator text expected behind 'withLineSeparator' of 'write' action.");
				return;
			}
			lineSeparator = config.macros.forEachTiddler.paramEncode(parameter[p]);
			p++;
		}
	}
	
	// Check for extra parameters
	if (parameter.length > p) {
		config.macros.forEachTiddler.createExtraParameterErrorElement(place, "write", parameter, p);
		return;
	}

	// Perform the action.
	var func = config.macros.forEachTiddler.getEvalTiddlerFunction(textExpression, context);
	var count = tiddlers.length;
	var text = "";
	if (count > 0 && beginExpression)
		text += config.macros.forEachTiddler.getEvalTiddlerFunction(beginExpression, context)(undefined, context, count, undefined);
	
	for (var i = 0; i < count; i++) {
		var tiddler = tiddlers[i];
		text += func(tiddler, context, count, i);
	}
	
	if (count > 0 && endExpression)
		text += config.macros.forEachTiddler.getEvalTiddlerFunction(endExpression, context)(undefined, context, count, undefined);

	if (count == 0 && noneExpression) 
		text += config.macros.forEachTiddler.getEvalTiddlerFunction(noneExpression, context)(undefined, context, count, undefined);
		

	if (filename) {
		if (lineSeparator !== undefined) {
			lineSeparator = lineSeparator.replace(/\\n/mg, "\n").replace(/\\r/mg, "\r");
			text = text.replace(/\n/mg,lineSeparator);
		}
		saveFile(filename, convertUnicodeToUTF8(text));
	} else {
		var wrapper = createTiddlyElement(place, "span");
		wikify(text, wrapper, null/* highlightRegExp */, context.inTiddler);
	}
};


// ---------------------------------------------------------------------------
//  Helpers
// ---------------------------------------------------------------------------

// Internal.
//
config.macros.forEachTiddler.createContext = function(placeParam, whereClauseParam, sortClauseParam, sortAscendingParam, actionNameParam, actionParameterParam, scriptText, tiddlyWikiPathParam, inTiddlerParam) {
	return {
		place : placeParam, 
		whereClause : whereClauseParam, 
		sortClause : sortClauseParam, 
		sortAscending : sortAscendingParam, 
		script : scriptText,
		actionName : actionNameParam, 
		actionParameter : actionParameterParam,
		tiddlyWikiPath : tiddlyWikiPathParam,
		inTiddler : inTiddlerParam, // the tiddler containing the <<forEachTiddler ...>> macro call.
		viewerTiddler : config.macros.forEachTiddler.getContainingTiddler(placeParam) // the tiddler showing the forEachTiddler result
	};
};

// Internal.
//
// Returns a TiddlyWiki with the tiddlers loaded from the TiddlyWiki of 
// the given path.
//
config.macros.forEachTiddler.loadTiddlyWiki = function(path, idPrefix) {
	if (!idPrefix) {
		idPrefix = "store";
	}
	var lenPrefix = idPrefix.length;
	
	// Read the content of the given file
	var content = loadFile(this.getLocalPath(path));
	if(content === null) {
		throw "TiddlyWiki '"+path+"' not found.";
	}
	
	var tiddlyWiki = new TiddlyWiki();

	// Starting with TW 2.2 there is a helper function to import the tiddlers
	if (tiddlyWiki.importTiddlyWiki) {
		if (!tiddlyWiki.importTiddlyWiki(content))
			throw "File '"+path+"' is not a TiddlyWiki.";
		tiddlyWiki.dirty = false;
		return tiddlyWiki;
	}
	
	// The legacy code, for TW < 2.2
	
	// Locate the storeArea div's
	var posOpeningDiv = content.indexOf(startSaveArea);
	var posClosingDiv = content.lastIndexOf(endSaveArea);
	if((posOpeningDiv == -1) || (posClosingDiv == -1)) {
		throw "File '"+path+"' is not a TiddlyWiki.";
	}
	var storageText = content.substr(posOpeningDiv + startSaveArea.length, posClosingDiv);
	
	// Create a "div" element that contains the storage text
	var myStorageDiv = document.createElement("div");
	myStorageDiv.innerHTML = storageText;
	myStorageDiv.normalize();
	
	// Create all tiddlers in a new TiddlyWiki
	// (following code is modified copy of TiddlyWiki.prototype.loadFromDiv)
	var store = myStorageDiv.childNodes;
	for(var t = 0; t < store.length; t++) {
		var e = store[t];
		var title = null;
		if(e.getAttribute)
			title = e.getAttribute("tiddler");
		if(!title && e.id && e.id.substr(0,lenPrefix) == idPrefix)
			title = e.id.substr(lenPrefix);
		if(title && title !== "") {
			var tiddler = tiddlyWiki.createTiddler(title);
			tiddler.loadFromDiv(e,title);
		}
	}
	tiddlyWiki.dirty = false;

	return tiddlyWiki;
};


	
// Internal.
//
// Returns a function that has a function body returning the given javaScriptExpression.
// The function has the parameters:
// 
//	 (tiddler, context, count, index)
//
config.macros.forEachTiddler.getEvalTiddlerFunction = function (javaScriptExpression, context) {
	var script = context["script"];
	var functionText = "var theFunction = function(tiddler, context, count, index) { return "+javaScriptExpression+"}";
	var fullText = (script ? script+";" : "")+functionText+";theFunction;";
	return eval(fullText);
};

// Internal.
//
config.macros.forEachTiddler.findTiddlers = function(whereClause, context, tiddlyWiki) {
	var result = [];
	var func = config.macros.forEachTiddler.getEvalTiddlerFunction(whereClause, context);
	tiddlyWiki.forEachTiddler(function(title,tiddler) {
		if (func(tiddler, context, undefined, undefined)) {
			result.push(tiddler);
		}
	});
	return result;
};

// Internal.
//
config.macros.forEachTiddler.createExtraParameterErrorElement = function(place, actionName, parameter, firstUnusedIndex) {
	var message = "Extra parameter behind '"+actionName+"':";
	for (var i = firstUnusedIndex; i < parameter.length; i++) {
		message += " "+parameter[i];
	}
	this.handleError(place, message);
};

// Internal.
//
config.macros.forEachTiddler.sortAscending = function(tiddlerA, tiddlerB) {
	var result = 
		(tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue) 
			? 0
			: (tiddlerA.forEachTiddlerSortValue < tiddlerB.forEachTiddlerSortValue)
			   ? -1 
			   : +1; 
	return result;
};

// Internal.
//
config.macros.forEachTiddler.sortDescending = function(tiddlerA, tiddlerB) {
	var result = 
		(tiddlerA.forEachTiddlerSortValue == tiddlerB.forEachTiddlerSortValue) 
			? 0
			: (tiddlerA.forEachTiddlerSortValue < tiddlerB.forEachTiddlerSortValue)
			   ? +1 
			   : -1; 
	return result;
};

// Internal.
//
config.macros.forEachTiddler.sortTiddlers = function(tiddlers, sortClause, ascending, context) {
	// To avoid evaluating the sortClause whenever two items are compared 
	// we pre-calculate the sortValue for every item in the array and store it in a 
	// temporary property ("forEachTiddlerSortValue") of the tiddlers.
	var func = config.macros.forEachTiddler.getEvalTiddlerFunction(sortClause, context);
	var count = tiddlers.length;
	var i;
	for (i = 0; i < count; i++) {
		var tiddler = tiddlers[i];
		tiddler.forEachTiddlerSortValue = func(tiddler,context, undefined, undefined);
	}

	// Do the sorting
	tiddlers.sort(ascending ? this.sortAscending : this.sortDescending);

	// Delete the temporary property that holds the sortValue.	
	for (i = 0; i < tiddlers.length; i++) {
		delete tiddlers[i].forEachTiddlerSortValue;
	}
};


// Internal.
//
config.macros.forEachTiddler.trace = function(message) {
	displayMessage(message);
};

// Internal.
//
config.macros.forEachTiddler.traceMacroCall = function(place,macroName,params) {
	var message ="<<"+macroName;
	for (var i = 0; i < params.length; i++) {
		message += " "+params[i];
	}
	message += ">>";
	displayMessage(message);
};


// Internal.
//
// Creates an element that holds an error message
// 
config.macros.forEachTiddler.createErrorElement = function(place, exception) {
	var message = (exception.description) ? exception.description : exception.toString();
	return createTiddlyElement(place,"span",null,"forEachTiddlerError","<<forEachTiddler ...>>: "+message);
};

// Internal.
//
// @param place [may be null]
//
config.macros.forEachTiddler.handleError = function(place, exception) {
	if (place) {
		this.createErrorElement(place, exception);
	} else {
		throw exception;
	}
};

// Internal.
//
// Encodes the given string.
//
// Replaces 
//	 "$))" to ">>"
//	 "$)" to ">"
//
config.macros.forEachTiddler.paramEncode = function(s) {
	var reGTGT = new RegExp("\\$\\)\\)","mg");
	var reGT = new RegExp("\\$\\)","mg");
	return s.replace(reGTGT, ">>").replace(reGT, ">");
};

// Internal.
//
// Returns the given original path (that is a file path, starting with "file:")
// as a path to a local file, in the systems native file format.
//
// Location information in the originalPath (i.e. the "#" and stuff following)
// is stripped.
// 
config.macros.forEachTiddler.getLocalPath = function(originalPath) {
	// Remove any location part of the URL
	var hashPos = originalPath.indexOf("#");
	if(hashPos != -1)
		originalPath = originalPath.substr(0,hashPos);
	// Convert to a native file format assuming
	// "file:///x:/path/path/path..." - pc local file --> "x:\path\path\path..."
	// "file://///server/share/path/path/path..." - FireFox pc network file --> "\\server\share\path\path\path..."
	// "file:///path/path/path..." - mac/unix local file --> "/path/path/path..."
	// "file://server/share/path/path/path..." - pc network file --> "\\server\share\path\path\path..."
	var localPath;
	if(originalPath.charAt(9) == ":") // pc local file
		localPath = unescape(originalPath.substr(8)).replace(new RegExp("/","g"),"\\");
	else if(originalPath.indexOf("file://///") === 0) // FireFox pc network file
		localPath = "\\\\" + unescape(originalPath.substr(10)).replace(new RegExp("/","g"),"\\");
	else if(originalPath.indexOf("file:///") === 0) // mac/unix local file
		localPath = unescape(originalPath.substr(7));
	else if(originalPath.indexOf("file:/") === 0) // mac/unix local file
		localPath = unescape(originalPath.substr(5));
	else // pc network file
		localPath = "\\\\" + unescape(originalPath.substr(7)).replace(new RegExp("/","g"),"\\");	
	return localPath;
};

// ---------------------------------------------------------------------------
// Stylesheet Extensions (may be overridden by local StyleSheet)
// ---------------------------------------------------------------------------
//
setStylesheet(
	".forEachTiddlerError{color: #ffffff;background-color: #880000;}",
	"forEachTiddler");

//============================================================================
// End of forEachTiddler Macro
//============================================================================


//============================================================================
// String.startsWith Function
//============================================================================
//
// Returns true if the string starts with the given prefix, false otherwise.
//
version.extensions["String.startsWith"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.startsWith = function(prefix) {
	var n =  prefix.length;
	return (this.length >= n) && (this.slice(0, n) == prefix);
};



//============================================================================
// String.endsWith Function
//============================================================================
//
// Returns true if the string ends with the given suffix, false otherwise.
//
version.extensions["String.endsWith"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.endsWith = function(suffix) {
	var n = suffix.length;
	return (this.length >= n) && (this.right(n) == suffix);
};


//============================================================================
// String.contains Function
//============================================================================
//
// Returns true when the string contains the given substring, false otherwise.
//
version.extensions["String.contains"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
String.prototype.contains = function(substring) {
	return this.indexOf(substring) >= 0;
};

//============================================================================
// Array.indexOf Function
//============================================================================
//
// Returns the index of the first occurance of the given item in the array or 
// -1 when no such item exists.
//
// @param item [may be null]
//
version.extensions["Array.indexOf"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.indexOf = function(item) {
	for (var i = 0; i < this.length; i++) {
		if (this[i] == item) {
			return i;
		}
	}
	return -1;
};

//============================================================================
// Array.contains Function
//============================================================================
//
// Returns true when the array contains the given item, otherwise false. 
//
// @param item [may be null]
//
version.extensions["Array.contains"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.contains = function(item) {
	return (this.indexOf(item) >= 0);
};

//============================================================================
// Array.containsAny Function
//============================================================================
//
// Returns true when the array contains at least one of the elements 
// of the item. Otherwise (or when items contains no elements) false is returned.
//
version.extensions["Array.containsAny"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.containsAny = function(items) {
	for(var i = 0; i < items.length; i++) {
		if (this.contains(items[i])) {
			return true;
		}
	}
	return false;
};


//============================================================================
// Array.containsAll Function
//============================================================================
//
// Returns true when the array contains all the items, otherwise false.
// 
// When items is null false is returned (even if the array contains a null).
//
// @param items [may be null] 
//
version.extensions["Array.containsAll"] = {major: 1, minor: 0, revision: 0, date: new Date(2005,11,20), provider: "http://tiddlywiki.abego-software.de"};
//
Array.prototype.containsAll = function(items) {
	for(var i = 0; i < items.length; i++) {
		if (!this.contains(items[i])) {
			return false;
		}
	}
	return true;
};


} // of "install only once"

// Used Globals (for JSLint) ==============
// ... DOM
/*global 	document */
// ... TiddlyWiki Core
/*global 	convertUnicodeToUTF8, createTiddlyElement, createTiddlyLink, 
			displayMessage, endSaveArea, hasClass, loadFile, saveFile, 
			startSaveArea, store, wikify */
//}}}


/***
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2005 ([[www.abego-software.de|http://www.abego-software.de]])

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.

Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
***/

/***
<<checkForDataTiddlerPlugin>>
|''Name:''|FormTiddlerPlugin|
|''Version:''|1.0.6 (2007-06-24)|
|''Source:''|http://tiddlywiki.abego-software.de/#FormTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
|''Macros:''|formTiddler, checkForDataTiddlerPlugin, newTiddlerWithForm|
|''Requires:''|DataTiddlerPlugin|
|''TiddlyWiki:''|1.2.38+, 2.0|
|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
!Description
Use form-based tiddlers to enter your tiddler data using text fields, listboxes, checkboxes etc. (All standard HTML Form input elements supported).

''Syntax:'' 
|>|{{{<<}}}''formTiddler'' //tiddlerName//{{{>>}}}|
|//tiddlerName//|The name of the FormTemplate tiddler to be used to edit the data of the tiddler containing the macro.|

|>|{{{<<}}}''newTiddlerWithForm'' //formTemplateName// //buttonLabel// [//titleExpression// [''askUser'']] {{{>>}}}|
|//formTemplateName//|The name of the tiddler that defines the form the new tiddler should use.|
|//buttonLabel//|The label of the button|
|//titleExpression//|A (quoted) JavaScript String expression that defines the title (/name) of the new tiddler.|
|''askUser''|Typically the user is not asked for the title when a title is specified (and not yet used). When ''askUser'' is given the user will be asked in any case. This may be used when the calculated title is just a suggestion that must be confirmed by the user|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|

For details and how to use the macros see the [[introduction|FormTiddler Introduction]] and the [[examples|FormTiddler Examples]].

!Revision history
* v1.0.6 (2007-06-24)
** Fixed problem when using SELECT component in Internet Explorer (thanks to MaikBoenig for reporting)
* v1.0.5 (2006-02-24)
** Removed "debugger;" instruction
* v1.0.4 (2006-02-07)
** Bug: On IE no data is written to data section when field values changed (thanks to KenGirard for reporting)
* v1.0.3 (2006-02-05)
** Bug: {{{"No form template specified in <<formTiddler>>"}}} when using formTiddler macro on InternetExplorer (thanks to KenGirard for reporting)
* v1.0.2 (2006-01-06)
** Support TiddlyWiki 2.0
* v1.0.1 (2005-12-22)
** Features: 
*** Support InternetExplorer
*** Added newTiddlerWithForm Macro
* v1.0.0 (2005-12-14)
** initial version

!Code
***/
//{{{

//============================================================================
//============================================================================
//						FormTiddlerPlugin
//============================================================================
//============================================================================

if (!window.abego) window.abego = {};

abego.getOptionsValue = function(element,i) {
	var v = element.options[i].value;
	if (!v && element.options[i].text)
		v = element.options[i].text;
	return v;
};

version.extensions.FormTiddlerPlugin = {
	major: 1, minor: 0, revision: 5,
	date: new Date(2006, 2, 24), 
	type: 'plugin',
	source: "http://tiddlywiki.abego-software.de/#FormTiddlerPlugin"
};

// For backward compatibility with v1.2.x
//
if (!window.story) window.story=window; 
if (!TiddlyWiki.prototype.getTiddler) TiddlyWiki.prototype.getTiddler = function(title) { return t = this.tiddlers[title]; return (t != undefined && t instanceof Tiddler) ? t : null; } 

//============================================================================
// formTiddler Macro
//============================================================================

// -------------------------------------------------------------------------------
// Configurations and constants 
// -------------------------------------------------------------------------------

config.macros.formTiddler = {
	// Standard Properties
	label: "formTiddler",
	version: {major: 1, minor: 0, revision: 4, date: new Date(2006, 2, 7)},
	prompt: "Edit tiddler data using forms",

	// Define the "setters" that set the values of INPUT elements of a given type
	// (must match the corresponding "getter")
	setter: {  
		button:				function(e, value) {/*contains no data */ },
		checkbox:			function(e, value) {e.checked = value;},
		file:				function(e, value) {try {e.value = value;} catch(e) {/* ignore, possibly security error*/}},
		hidden:				function(e, value) {e.value = value;},
		password:			function(e, value) {e.value = value;},
		radio:				function(e, value) {e.checked = (e.value == value);},
		reset:				function(e, value) {/*contains no data */ },
		"select-one":		function(e, value) {config.macros.formTiddler.setSelectOneValue(e,value);},
		"select-multiple":	function(e, value) {config.macros.formTiddler.setSelectMultipleValue(e,value);},
		submit:				function(e, value) {/*contains no data */},
		text:				function(e, value) {e.value = value;},
		textarea:			function(e, value) {e.value = value;}
	},

	// Define the "getters" that return the value of INPUT elements of a given type
	// Return undefined to not store any data.
	getter: {  
		button:				function(e, value) {return undefined;},
		checkbox:			function(e, value) {return e.checked;},
		file:				function(e, value) {return e.value;},
		hidden:				function(e, value) {return e.value;},
		password:			function(e, value) {return e.value;},
		radio:				function(e, value) {return e.checked ? e.value : undefined;},
		reset:				function(e, value) {return undefined;},
		"select-one":		function(e, value) {return config.macros.formTiddler.getSelectOneValue(e);},
		"select-multiple":	function(e, value) {return config.macros.formTiddler.getSelectMultipleValue(e);},
		submit:				function(e, value) {return undefined;},
		text:				function(e, value) {return e.value;},
		textarea:			function(e, value) {return e.value;}
	}
};


// -------------------------------------------------------------------------------
// The formTiddler Macro Handler 
// -------------------------------------------------------------------------------

config.macros.formTiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
	if (!config.macros.formTiddler.checkForExtensions(place, macroName)) {
		return;
	}
	// --- Parsing ------------------------------------------

	var i = 0; // index running over the params

	// get the name of the form template tiddler
	var formTemplateName = undefined;
	if (i < params.length) {
		formTemplateName = params[i];
		i++;
	}

	if (!formTemplateName) {
		config.macros.formTiddler.createErrorElement(place, "No form template specified in <<" + macroName + ">>.");
		return;
	}


	// --- Processing ------------------------------------------

	// Get the form template text. 
	// (This contains the INPUT elements for the form.)
	var formTemplateTiddler = store.getTiddler(formTemplateName);
	if (!formTemplateTiddler) {
		config.macros.formTiddler.createErrorElement(place, "Form template '" + formTemplateName + "' not found.");
		return;
	}
	var templateText = formTemplateTiddler.text;
	if(!templateText) {
		// Shortcut: when template text is empty we do nothing.
		return;
	}

	// Get the name of the tiddler containing this "formTiddler" macro
	// (i.e. the tiddler, that will be edited and that contains the data)
	var tiddlerName = config.macros.formTiddler.getContainingTiddlerName(place);

	// Append a "form" element. 
	var formName = "form"+formTemplateName+"__"+tiddlerName;
	var e = document.createElement("form");
	e.setAttribute("name", formName);
	place.appendChild(e);

	// "Embed" the elements defined by the templateText (i.e. the INPUT elements) 
	// into the "form" element we just created
	wikify(templateText, e);

	// Initialize the INPUT elements.
	config.macros.formTiddler.initValuesAndHandlersInFormElements(formName, DataTiddler.getDataObject(tiddlerName));
}


// -------------------------------------------------------------------------------
// Form Data Access 
// -------------------------------------------------------------------------------

// Internal.
//
// Initialize the INPUT elements of the form with the values of their "matching"
// data fields in the tiddler. Also setup the onChange handler to ensure that
// changes in the INPUT elements are stored in the tiddler's data.
//
config.macros.formTiddler.initValuesAndHandlersInFormElements = function(formName, data) {
	// config.macros.formTiddler.trace("initValuesAndHandlersInFormElements(formName="+formName+", data="+data+")");

	// find the form
	var form = config.macros.formTiddler.findForm(formName);
	if (!form) {
		return;
	}

	try {
		var elems = form.elements;
		for (var i = 0; i < elems.length; i++) {
			var c = elems[i];
		
			var setter = config.macros.formTiddler.setter[c.type];
			if (setter) {
				var value = data[c.name];
				if (value != null) {
					setter(c, value);
				}
				c.onchange = onFormTiddlerChange;
			} else {
				config.macros.formTiddler.displayFormTiddlerError("No setter defined for INPUT element of type '"+c.type+"'. (Element '"+c.name+"' in form '"+formName+"')");
			}
		}
	} catch(e) {
		config.macros.formTiddler.displayFormTiddlerError("Error when updating elements with new formData. "+e);
	}
}


// Internal.
//
// @return [may be null]
//
config.macros.formTiddler.findForm = function(formName) {
	// We must manually iterate through the document's forms, since
	// IE does not support the "document[formName]" approach

	var forms = window.document.forms;
	for (var i = 0; i < forms.length; i++) {
		var form = forms[i];
		if (form.name == formName) {
			return form;
		}
	}

	return null;
}


// Internal.
//
config.macros.formTiddler.setSelectOneValue = function(element,value) {
	var n = element.options.length;
	for (var i = 0; i < n; i++) {
		element.options[i].selected = abego.getOptionsValue(element,i) == value;
	}
}

// Internal.
//
config.macros.formTiddler.setSelectMultipleValue = function(element,value) {
	var values = {};
	for (var i = 0; i < value.length; i++) {
		values[value[i]] = true;
	}
	
	var n = element.length;
	for (var i = 0; i < n; i++) {
		element.options[i].selected = !(!values[abego.getOptionsValue(element,i)]);
	}
}

// Internal.
//
config.macros.formTiddler.getSelectOneValue = function(element) {
	var i = element.selectedIndex;
	return (i >= 0) ? abego.getOptionsValue(element,i) : null;
}

// Internal.
//
config.macros.formTiddler.getSelectMultipleValue = function(element) {
	var values = [];
	var n = element.length;
	for (var i = 0; i < n; i++) {
		if (element.options[i].selected) {
			values.push(abego.getOptionsValue(element,i));
		}
	}
	return values;
}



// -------------------------------------------------------------------------------
// Helpers 
// -------------------------------------------------------------------------------

// Internal.
//
config.macros.formTiddler.checkForExtensions = function(place,macroName) {
	if (!version.extensions.DataTiddlerPlugin) {
		config.macros.formTiddler.createErrorElement(place, "<<" + macroName + ">> requires the DataTiddlerPlugin. (You can get it from http://tiddlywiki.abego-software.de/#DataTiddlerPlugin)");
		return false;
	}
	return true;
}

// Internal.
//
// Displays a trace message in the "TiddlyWiki" message pane.
// (used for debugging)
//
config.macros.formTiddler.trace = function(s) {
	displayMessage("Trace: "+s);
}

// Internal.
//
// Display some error message in the "TiddlyWiki" message pane.
//
config.macros.formTiddler.displayFormTiddlerError = function(s) {
	alert("FormTiddlerPlugin Error: "+s);
}

// Internal.
//
// Creates an element that holds an error message
// 
config.macros.formTiddler.createErrorElement = function(place, message) {
	return createTiddlyElement(place,"span",null,"formTiddlerError",message);
}

// Internal.
//
// Returns the name of the tiddler containing the given element.
// 
config.macros.formTiddler.getContainingTiddlerName = function(element) {
	return story.findContainingTiddler(element).id.substr(7);
}

// -------------------------------------------------------------------------------
// Event Handlers 
// -------------------------------------------------------------------------------

// This function must be called by the INPUT elements whenever their
// data changes. Typically this is done through an "onChange" handler.
//
function onFormTiddlerChange (e) {
	// config.macros.formTiddler.trace("onFormTiddlerChange "+e);

	if (!e) var e = window.event;

	var target = resolveTarget(e);
	var tiddlerName = config.macros.formTiddler.getContainingTiddlerName(target);
	var getter = config.macros.formTiddler.getter[target.type];
	if (getter) {
		var value = getter(target);
		DataTiddler.setData(tiddlerName, target.name, value);
	} else {
		config.macros.formTiddler.displayFormTiddlerError("No getter defined for INPUT element of type '"+target.type+"'. (Element '"+target.name+"' used in tiddler '"+tiddlerName+"')");
	}
}

// ensure that the function can be used in HTML event handler
window.onFormTiddlerChange = onFormTiddlerChange;


// -------------------------------------------------------------------------------
// Stylesheet Extensions (may be overridden by local StyleSheet)
// -------------------------------------------------------------------------------

setStylesheet(
	".formTiddlerError{color: #ffffff;background-color: #880000;}",
	"formTiddler");


//============================================================================
// checkForDataTiddlerPlugin Macro
//============================================================================

config.macros.checkForDataTiddlerPlugin = {
	// Standard Properties
	label: "checkForDataTiddlerPlugin",
	version: {major: 1, minor: 0, revision: 0, date: new Date(2005, 12, 14)},
	prompt: "Check if the DataTiddlerPlugin exists"
}

config.macros.checkForDataTiddlerPlugin.handler = function(place,macroName,params) {
	config.macros.formTiddler.checkForExtensions(place, config.macros.formTiddler.label);
}



//============================================================================
// newTiddlerWithForm Macro
//============================================================================

config.macros.newTiddlerWithForm = {
	// Standard Properties
	label: "newTiddlerWithForm",
	version: {major: 1, minor: 0, revision: 1, date: new Date(2006, 1, 6)},
	prompt: "Creates a new Tiddler with a <<formTiddler ...>> macro"
}

config.macros.newTiddlerWithForm.handler = function(place,macroName,params) {
	// --- Parsing ------------------------------------------

	var i = 0; // index running over the params

	// get the name of the form template tiddler
	var formTemplateName = undefined;
	if (i < params.length) {
		formTemplateName = params[i];
		i++;
	}

	if (!formTemplateName) {
		config.macros.formTiddler.createErrorElement(place, "No form template specified in <<" + macroName + ">>.");
		return;
	}

	// get the button label
	var buttonLabel = undefined;
	if (i < params.length) {
		buttonLabel = params[i];
		i++;
	}

	if (!buttonLabel) {
		config.macros.formTiddler.createErrorElement(place, "No button label specified in <<" + macroName + ">>.");
		return;
	}

	// get the (optional) tiddlerName script and "askUser"
	var tiddlerNameScript = undefined;
	var askUser = false;
	if (i < params.length) {
		tiddlerNameScript = params[i];
		i++;

		if (i < params.length && params[i] == "askUser") {
			askUser = true;
			i++;
		}
	}

	// --- Processing ------------------------------------------

	if(!readOnly) {
		var onClick = function() {
			var tiddlerName;
			if (tiddlerNameScript) {
				try {
					tiddlerName = eval(tiddlerNameScript);
				} catch (ex) {
				}
			}
			if (!tiddlerName || askUser) {
				tiddlerName = prompt("Please specify a tiddler name.", askUser ? tiddlerName : "");
			}
			while (tiddlerName && store.getTiddler(tiddlerName)) {
				tiddlerName = prompt("A tiddler named '"+tiddlerName+"' already exists.\n\n"+"Please specify a tiddler name.", tiddlerName);
			}

			// tiddlerName is either null (user canceled) or a name that is not yet in the store.
			if (tiddlerName) {
				var body = "<<formTiddler [["+formTemplateName+"]]>>";
				var tags = [];
				store.saveTiddler(tiddlerName,tiddlerName,body,config.options.txtUserName,new Date(),tags);
				story.displayTiddler(null,tiddlerName,1);
			}
		}

		createTiddlyButton(place,buttonLabel,buttonLabel,onClick);
    }
}

//}}}


/***
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2005 ([[www.abego-software.de|http://www.abego-software.de]])

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.

Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
***/
Below is sample code for getting the version number of the K2 software. 


{{{
[C#]
string version
appServerMgrProxy.GetMSeriesIdInfo( IdComp.SW_VER, out version);
Console.WriteLine("K2 Software Version: " + version);
}}}
{{{
[C++]
BSTR version;
spAppServerMgrProxy->GetMSeriesIdInfo( IdComp_SW_VER, &version);
CString sVersion = version;
printf("K2 Software Version: %s\n", sVersion);
}}}
The sample code below shows how you can get a list of clips in a bin and then get properties of those clips. The way you do it is:

1. Call ~EnumerateAssets to create a list of the clips in a bin
2. Query the list for the clip properties you want ([[click here|Asset properties]] for list of asset properties you can query).
3. Print the results.

{{{
[C#]
// create a mediamgr object
IMediaMgr mediaMgr = iappServer.CreateMediaMgr(appName);

// enumerate the assets in the v:\default bin
int count = 0;
int cookie = mediaMgr.EnumerateAssets("V:", "default", ref count);
 
// let's query for 3 properties of the clip: the name, video compression type,
// and the length
string query = "name+videoCompressionType+lengthstr";
 
// Other properties you can query:
// "uri", "name", "assettype", "created", "modified", "attributes1", 
// "attributes2", "videoformat", "videocompressiontype", "firsttimecodemask", 
// "lengthstr", "maxlength", "markin", "markout", "markinstr", "markoutstr", 
// "dropframe", "locked", "hidef"
// for other properties search the guide for "Asset properties" 

// Let's only get info on the first 100 clips in the bin
int maxCount = 100;
object dataObj = null;

// Now, let's query for the results we want: 
// query it for the properties we setup above, 
// start with the 0th element, 
// go up to a max of 100 clips
// capture the actual number of clips we're getting data back from,
// and return the results in a data object
mediaMgr.GetResultProperty( cookie, query, 0, maxCount, out count, out dataObj);

Console.WriteLine("Found " + count + " clips in V:\\default\\");

// GetResultProperty for clip data returns a 2 dimensional array
// - the first dimension represents the clip
// - the second dimension represents the properties of the clip

// so let's cast the dataobj to a clip array first
object[] clipArray = (object[]) dataObj; 

// loop thru each clip
foreach (object clip in clipArray)
{
 // then cast the clip object to an array of properties for each clip
 object[] propertyArray = (object[]) clip;
 
 // and finally print out the clip properties for each clip
 Console.WriteLine("Clip: {0} Created: {1} Length: {2}", 
 propertyArray[0], propertyArray[1], propertyArray[2]);
}

// make sure that you close the results after enumerating volumes, bins, or assets.
// if you don't, you will run out of database selectors!
mediaMgr.CloseResults(cookie);
mediaMgr.Dispose();
}}}
{{{
[C++]
// create a mediamgr object
IMediaMgrPtr spMediaMgr;
HRESULT hr = spAppServer->CreateMediaMgr(m_bstrAppName, &spMediaMgr);

// enumerate the assets in the v:\default bin
long cookie;
long count = 0;
BSTR bstrXmlData = NULL; // must set this to NULL!

spMediaMgr->EnumerateAssets(_bstr_t("V:"), _bstr_t("default"), &count, &cookie);

// let's query for 3 properties of the clip: the name, video compression type,
// and the length
_bstr_t sQuery("name+videoCompressionType+lengthstr");

// Other properties you can query:
// "uri", "name", "assettype", "created", "modified", "attributes1", 
// "attributes2", "videoformat", "videocompressiontype", "firsttimecodemask", 
// "lengthstr", "maxlength", "markin", "markout", "markinstr", "markoutstr", 
// "dropframe", "locked", "hidef"
// for other properties search the guide for "Asset properties" 

// Let's only get info on the first 100 clips in the bin
int maxCount = 100;

// Now, let's query for the results we want: 
// query it for the properties we setup above, 
// start with the 0th element, 
// go up to a max of 100 clips
// capture the actual number of clips we're getting data back from,
// and return the results in a data object
VARIANT varResult;
spMediaMgr->GetResultProperty( cookie, sQuery, 0, maxCount, &count, &varResult);

printf("Found %d clips in V:\\default\\\n", count);

// create a safe array for the clips
SAFEARRAY *pClipArray = varResult.parray;
VARIANT *pVar = NULL;
long arrClipIndex[1];

// get the first clip
arrClipIndex[0] = 0;
SafeArrayPtrOfIndex(pClipArray, arrClipIndex, (void **)&pVar);

// create a safe array for the clip's properties
SAFEARRAY* pPropertyArray = pVar->parray;
long arrPropertyIndex[1];

// get the first property
arrPropertyIndex[0] = 0;
SafeArrayPtrOfIndex(pPropertyArray, arrPropertyIndex, (void **)&pVar);
CString sName = pVar->bstrVal;

// get the second property
arrPropertyIndex[0] = 1;
SafeArrayPtrOfIndex(pPropertyArray, arrPropertyIndex, (void **)&pVar);
CString sVideoCompressionType = pVar->bstrVal;

// get the third property
arrPropertyIndex[0] = 2;
SafeArrayPtrOfIndex(pPropertyArray, arrPropertyIndex, (void **)&pVar);
CString sLength = pVar->bstrVal;

printf("Clip name: %s video compression type: %s length: %s\n\n", sName, sVideoCompressionType, sLength);

// etc...

// make sure that you close the results after enumerating volumes, bins, or assets.
// if you don't, you will run out of database selectors!
spMediaMgr->CloseResults(cookie);
}}}
The ~IMediaMgr::~GetProperty function can be used to return one or more asset properties. [[Click here for a list of all asset properties|Asset properties]].

@@color(red):Note that if you need to get multiple properties it is more efficient to make one ~GetProperty call for all properties rather than multiple ~GetProperty calls for each property.@@

{{{
[C#]
IMediaMgr mediaMgr = iappServer.CreateMediaMgr(appName);

// get a single property
string assetType = (string) mediaMgr.GetProperty("edl/cmf//local/V:/default/Clip", 
 "assetType");
Console.WriteLine("asset type = " + assetType);


// get multiple properties
object[] results = (object[]) mediaMgr.GetProperty("edl/cmf//local/V:/default/Clip",
 "LengthStr+videoCompressionType+videoFormatStr");

foreach (object obj in results)
{
Console.WriteLine("obj = " + obj);
}

// cleanup
mediaMgr.Dispose();
}}}
{{{
[C++]
IMediaMgrPtr spMediaMgr;
hr = spAppServer->CreateMediaMgr(_bstr_t("YourApplicationName"), 
 &spMediaMgr);

VARIANT varResult;
// get a single property
hr = spMediaMgr->GetProperty(_bstr_t("edl/cmf//local/V:/default/Clip"), 
 _bstr_t("assetType"), &varResult);

CString sAssetType = varResult.bstrVal; 
printf("asset type: %s\n", sAssetType);


// get multiple properties
hr = spMediaMgr->GetProperty(_bstr_t("edl/cmf//local/V:/default/Clip"), 
 _bstr_t("LengthStr+videoCompressionType+videoFormatStr"), &varResult);

SAFEARRAY *pArray = varResult.parray;
VARIANT *pVar = NULL;
long arrIndex[1];

// get the first property
arrIndex[0] = 0;
SafeArrayPtrOfIndex(pArray, arrIndex, (void **)&pVar);
CString sLength = pVar->bstrVal; 

// get the second property
arrIndex[0] = 1;
SafeArrayPtrOfIndex(pArray, arrIndex, (void **)&pVar);
CString sVideoCompressionType = pVar->bstrVal; 

// get the third property
arrIndex[0] = 2;
SafeArrayPtrOfIndex(pArray, arrIndex, (void **)&pVar);
CString sVideoFormat = pVar->bstrVal; 

printf("Length: %s VideoCompressionType: %s VideoFormat: %s\n\n", 
 sLength, sVideoCompressionType, sVideoFormat);
}}}
Below is sample code showing how to get a thumbnail image for an asset and then save it in various formats.

{{{
[C#]
// get a media manager object
using (IMediaMgr mediaMgr = iappServer.CreateMediaMgr(appName) )
{
	// get thumbnail byte array for the asset V:/default/Clip
	System.Byte[] imageBytes = (System.Byte[]) mediaMgr.GetThumbnail(
		"edl/cmf//local/V:/default/Clip");

	// create a memory stream from the bytes
	using ( MemoryStream ms = new MemoryStream(imageBytes) )
	{
		// create bitmap from memory stream
		using ( Bitmap bitmap = new Bitmap(ms) )
		{
			// save bitmap in various formats
			bitmap.Save("bitmap.jpg", ImageFormat.Jpeg);
			bitmap.Save("bitmap.gif", ImageFormat.Gif);
			bitmap.Save("bitmap.png", ImageFormat.Png);
			bitmap.Save("bitmap.bmp", ImageFormat.Bmp);
		}
	}
}			
}}}
Below is sample code to find out who owns a K2's channel. The information returned includes the machine, the suite name, the user's name, and application that owns the channel.

{{{
[C#]
// make sure the remote system is up and running first
string channel = "C1";
string machine = "";
string suite = "";
string user = "";
string app = "";

appServerMgrProxy.GetChannelOwnerInfo(channel, out machine, out suite,
out user, out app);

Console.WriteLine("machine = {0} suite = {1} user = {2} app = {3}",
machine, suite, user, app);
}}}
{{{
[C++]
_variant_t varChan("C1");
BSTR bstrMachine;
BSTR bstrSuite;
BSTR bstrUser;
BSTR bstrApp;

spAppServerMgrProxy->GetChannelOwnerInfo(varChan, &bstrMachine, &bstrSuite, 
 &bstrUser, &bstrApp);

CString sMachine = bstrMachine;
CString sSuite = bstrSuite;
CString sUser = bstrUser;
CString sApp = bstrApp;

printf("machine = %s suite = %s user = %s app = %s\n", sMachine, sSuite, 
 sUser, sApp);
}}}
Channel status is provided for each K2 channel primarily by channel objects which control hardware resources and timeline attributes. Additional status info is provided by the application that owns the channel. This includes a thumbnail, custom text, and a format string called the status template. Channel status can be accessed by any number of Channel Status properties: some reporting status, some providing it. [[Click here for a list of all Channel Status properties|Channel Status properties]].

@@color(red):Note that if you need to get multiple properties it is more efficient to make one ~GetStatus call for all properties rather than multiple ~GetStatus calls for each property.@@

{{{
[C#]
// create a chanstatus object
string appName = "YourApplicationName";
string channel = "C1";
IChanStatus ichanStatus = iappServer.CreateChanStatus(appName, channel);

//////////////////////////
// get a single property
//////////////////////////
string timeremain = (string) ichanStatus.GetStatus("timeremainingstr");
Console.WriteLine("timeremain = " + timeremain);

///////////////////////////
// get multiple properties
///////////////////////////
string query = "ownerName+assetname+videoFormatStr";
object[] results = (object[]) ichanStatus.GetStatus(query);
foreach (object obj in results)
{
 Console.WriteLine("obj = " + obj);
}

// cleanup
ichanStatus.Dispose();
}}}
{{{
[C++]
IChanStatusPtr spChanStatus;
HRESULT hr = spAppServer->CreateChanStatus(_bstr_t("YourApplicationName"), 
 _bstr_t("C1"), &spChanStatus);
 
//////////////////////////
// get a single property
//////////////////////////
VARIANT varResult;
hr = spChanStatus->GetStatus(_bstr_t("timeremainingstr"), &varResult);

CString sTimeRemaining = varResult.bstrVal; 
printf("TimeRemaining: %s\n", sTimeRemaining);


///////////////////////////
// get multiple properties
///////////////////////////
hr = spChanStatus->GetStatus(_bstr_t("ownerName+assetname+videoFormatStr"), 
 &varResult);

SAFEARRAY *pArray = varResult.parray;
VARIANT *pVar = NULL;
long arrIndex[1];

// get the first property
arrIndex[0] = 0;
SafeArrayPtrOfIndex(pArray, arrIndex, (void **)&pVar);
String sOwnerName = pVar->bstrVal; 

// get the second property
arrIndex[0] = 1;
SafeArrayPtrOfIndex(pArray, arrIndex, (void **)&pVar);
CString sAssetName = pVar->bstrVal; 

// get the third property
arrIndex[0] = 2;
SafeArrayPtrOfIndex(pArray, arrIndex, (void **)&pVar);
CString sVideoFormat = pVar->bstrVal; 

printf("OwnerName: %s AssetName: %s VideoFormat: %s\n\n", sOwnerName, 
 sAssetName, sVideoFormat);
}}}
The configuration information on a K2 Client is stored in the file  C:\profile\config\config.xml. It has a lot of information in it, but the basic structure looks like this: 
{{{
<Config>
  <System model="K2-SD-04" ... />
  <Panel ... />
  <Board .../>
  <Version ... />
  <Channel id="C1" channelIndex="0" channelType="PlayerRecorder" ... />
  <Channel id="C2" channelIndex="1" channelType="PlayerRecorder" ... /> 
  <Channel id="C3" channelIndex="2" channelType="PlayerRecorder" ... />
  <Channel id="C4" channelIndex="3" channelType="PlayerRecorder" ... />
  <Gang index="0" ... />
  <Gang index="1" ... />
</Config> 
}}}

Below is sample code demonstrating how to retrieve the configuration data and extract the id and channelIndex information for each channel:
{{{
[C#]
// create ConfigMgr
string xmlConfig = "";
IConfigMgr iconfigMgr = iappServer.CreateConfigMgr(appName);

// get config xml document
iconfigMgr.GetConfigXml(out xmlConfig);

// load the xml string into a native XmlDocument
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlConfig);

// setup a namespace manager to parse the xml doc
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(xmlDoc.NameTable);
namespaceManager.AddNamespace("ns", "x-schema:#Schema1");

// get all channels
XmlNodeList channelList = xmlDoc.SelectNodes(("./Config/Channel"), namespaceManager);
if (channelList.Count > 0)
{
	IEnumerator channelNodeEnum = channelList.GetEnumerator();
	bool bMoreChannels = channelNodeEnum.MoveNext();

	// loop thru channels to find their id and channelIndex values
	Console.WriteLine("Channel names:");
	do
	{
		XmlNode channelNode = (XmlNode)channelNodeEnum.Current;
		string channelId = channelNode.Attributes.GetNamedItem("id").Value;
		int channelIndex = Int32.Parse(channelNode.Attributes.GetNamedItem("channelIndex").Value);
		Console.WriteLine("channel index: {0} id: {1}", channelIndex, channelId);

		bMoreChannels = channelNodeEnum.MoveNext();
	} while (bMoreChannels);
}				
}}}
For ~GetProperty or ~GetStatus calls, multiple properties can be requested by putting more than one string in the property query. This is more efficient than making multiple single property queries. 

Property names should be separated by white space or the ‘+’ character. When a single parameter is requested, an object (for C#) or variant (for C++) is returned of the appropriate type. When returning multiple values, an object array (for C#) or a variant of variants (for C++) is returned where each contained object or variant is of the appropriate type. The order of elements in this array matches the order in the property query.

See ~MediaMgr's [[Get asset properties]] and ~ChanStatus's [[Get channel status properties]] for examples.
Below is sample code for checking out the status of the K2. If the K2 is running and it has finished it's startup sequence, you should see the following values returned:

~SystemMode: Normal
~BootStage: ~UP_AND_RUNNING
~BootCode: NORMAL

Check the [[Status namespace|API/GrassValley.Mseries.Status.html]] for a list of other ~BootStage and ~BootCode values.

{{{
[C#]
// make sure the remote system is up and running first
string systemMode;
BootStage bootStage;
BootCode bootCode;
appServerMgrProxy.GetSystemStatus(out systemMode, out bootStage, out bootCode);
Console.WriteLine("SystemMode = {0} BootStage = {1} BootCode = {2}", systemMode, 
bootStage, bootCode);
}}}
{{{
[C++]
BSTR systemMode;
BootStage bootStage;
BootCode bootCode;
spAppServerMgrProxy->GetSystemStatus(&systemMode, &bootStage, &bootCode);
CString sSystemMode = systemMode;
printf("SystemMode: %s BootStage: %d BootCode: %d\n\n", sSystemMode, 
bootStage, bootCode);
}}}
Some ~MediaMgr calls are asynchronous calls. The way they work is that you make a call that returns a cookie and then make subsequent calls using that cookie to get results back. @@color(red):Warning: for any call that returns a cookie, you must call ~CloseResults on that cookie when you are finished using it. If you do not, you will soon run out of tokens and your calls will throw exceptions!@@

~EnumerateAssets is an asynchronous call. Calling ~EnumerateAssets on a volume and bin will return a cookie. Use that cookie to make ~GetXmlResults calls to get results about the assets. ~GetXmlResults takes a starting index variable and maxCount variable that specify how many results to return. The K2 server internally builds a ~DOMDocument that contains the results. The performance of building ~DOMDocuments is good for small documents, but suffers as it grows large. The end result is that the ~GetXmlResults calls will take a long time to return for large maxCounts. For best results, you should iteratively call ~GetXmlResults with small maxCount sizes using code similar to the code below.

To illustrate this, for a bin with 3500 clips in it, these are the total times for iteratively calling ~GetXmlResults for all assets using different maxCount sizes:

|>|>| !~GetXmlResults timings for 3500 clips |
| maxCount | iterations | total time (in seconds) |
| 128 | 28 | 05.78 |
| 256 | 14 | 05.77 |
| 512 | 7 | 05.89 |
| 1024 | 4 | 06.64 |
| 2048 | 2 | 09.05 |
| 4096 | 1 | 24.55 |

Given the results, it is recommended to always use a maxCount value no larger than 512 and iteratively calling ~GetXmlResults when necessary.

@@color(red):Warning:@@ Another issue to consider is that calls that build ~DOMDocuments on the K2 server impact performance. The more simultaneous ~GetXmlResults calls that you make, the slower calls will return. Also, ~MediaMgr will only allow 10 cookies open at a time. The 11th request will error so, as stated before, make sure you call ~CloseResults on cookies when you are finished using them.

So, if you are writing a client that needs to enumerate assets on several directories, it is recommended that you build functionality in your code that both throttles the number of simultaneous XML calls and caches the data. You should also then rely on ~GetXmlChanges to keep your cache information up-to-date rather than calling ~EnumerateAssets again as ~GetXmlChanges is a much lighter, faster call.

See [[Getting event notifications]] for an example.
The image below is the channel ganging window in ~AppCenter Pro configuration. You will see the Ganging option in the Configuration panel only if you have ~AppCenter Pro licensed for your K2. Contact ''[[GVDeveloperSupport@thomson.net|mailto:GVDeveloperSupport@thomson.net?subject=AppCenter Pro and Ganging]]'' for more information.

[img[Ganging configuration panel|images/ganging.png]]

To get or set channel ganging properties via the ~AppServer API do the following:

If you do not want to own the channel, you can query whether a channel is ganged and to which gang it belongs from the channel status interface:
{{{
[C#]
string appName = "YourApplicationName";
string channel = "C1";
 
// create a chanstatus object
IChanStatus ichanStatus = iappServer.CreateChanStatus(appName, channel);

// get multiple properties
object[] results = (object[]) ichanStatus.GetStatus("ganged+gangindex");

// "ganged" is either true or false
// not ganged: "gangindex" == -1; gang 1: "gangindex" == 0; gang 2: "gangindex" == 1
foreach (object obj in results)
{
 Console.WriteLine("obj = " + obj);
}
 
ichanStatus.Dispose();
}}}
{{{
[C++]
// create a chanstatus object
IChanStatusPtr spChanStatus;
HRESULT hr = spAppServer->CreateChanStatus(_bstr_t("YourApplicationName"), 
 _bstr_t("C1"), &spChanStatus);
 
VARIANT varResult;
// get multiple properties
hr = spChanStatus->GetStatus(_bstr_t("ganged+gangindex"), &varResult);

// "ganged" is either true or false
// not ganged: "gangindex" == -1; gang 1: "gangindex" == 0; gang 2: "gangindex" == 1
SAFEARRAY *pArray = varResult.parray;
VARIANT *pVar = NULL;
long arrIndex[1];

arrIndex[0] = 0;
SafeArrayPtrOfIndex(pArray, arrIndex, (void **)&pVar);
BOOL bGanged = (pVar->boolVal == VARIANT_TRUE);

arrIndex[0] = 1;
SafeArrayPtrOfIndex(pArray, arrIndex, (void **)&pVar);
int nGangIndex= pVar->iVal;

printf("Ganged: %d Gang Index: %d\n\n", bGanged, nGangIndex);

hr = spController->CloseChannel();
}}}

If you want to set the gang properties, you'll need to create a controller for the channel and set or get properties like this:
{{{
[C#]
string appName = "YourApplicationName";
string channel = "C1";
bool isNewController = false;
 
// create a controller
ISimpleController icontroller = iappServer.CreateController(
 appName, channel, out isNewController);

// not ganged: set "gangindex" = -1
// gang 1: set "gangindex" = 0
// gang 2: set "gangindex" = 1
icontroller.SetChannelProperty("gangindex", 0);
 
// to use a single channel to control the gang: set "singlecontroller" = true
icontroller.SetChannelProperty("singlecontroller", true);

// to record audio from more than one channel: set "consolidateaudio" = true
icontroller.SetChannelProperty("consolidateaudio", false);

// to record video from more than one channel: set "consolidatevideo" = true
icontroller.SetChannelProperty("consolidatevideo", true);

// get all gang properties of the channel
// note: currently the "singlecontroller" property only returns
// a valid value if the channel IS ganged.
string query = "ganged+gangindex+singlecontroller+consolidatevideo+consolidateaudio";
object[] results = (object[]) icontroller.GetChannelProperty(query);

// print all returned property values
foreach (object obj in results)
{
 Console.WriteLine("obj = " + obj);
}
}}}
{{{
[C++]
short nIsNewController;
ISimpleControllerPtr spController;

HRESULT hr = spAppServer->CreateController(_bstr_t("YourApplicationName"), 
 _bstr_t("C1"), &nIsNewController, &spController);

// not ganged: set "gangindex" = -1
// gang 1: set "gangindex" = 0
// gang 2: set "gangindex" = 1
spController->SetChannelProperty(_bstr_t("gangindex"), 0);

// tp use a single channel to control the gang: set "singlecontroller" = true
VARIANT_BOOL singlecontroller(FALSE);
spController->SetChannelProperty(_bstr_t("singlecontroller"), singlecontroller);

// to record audio from more than one channel: set "consolidateaudio" = true
VARIANT_BOOL consolidateaudio(FALSE);
spController->SetChannelProperty(_bstr_t("consolidateaudio"), consolidateaudio);

// to record video from more than one channel: set "consolidatevideo" = true
VARIANT_BOOL consolidatevideo(FALSE);
spController->SetChannelProperty(_bstr_t("consolidatevideo"), consolidatevideo);

VARIANT varResult;
// get all gang properties of the channel
// note: currently the "singlecontroller" property only returns
// a valid value if the channel IS ganged.
hr = spController->GetChannelProperty(_bstr_t("ganged+gangindex+singlecontroller+consolidatevideo+consolidateaudio"),
 &varResult);

SAFEARRAY *pArray = varResult.parray;
VARIANT *pVar = NULL;
long arrIndex[1];

arrIndex[0] = 0;
SafeArrayPtrOfIndex(pArray, arrIndex, (void **)&pVar);
BOOL bGanged = (pVar->boolVal == VARIANT_TRUE);

arrIndex[0] = 1;
SafeArrayPtrOfIndex(pArray, arrIndex, (void **)&pVar);
int nGangIndex= pVar->iVal;

arrIndex[0] = 2;
SafeArrayPtrOfIndex(pArray, arrIndex, (void **)&pVar);
BOOL bSingleController = (pVar->boolVal == VARIANT_TRUE);

arrIndex[0] = 3;
SafeArrayPtrOfIndex(pArray, arrIndex, (void **)&pVar);
BOOL bConsolidateVideo = (pVar->boolVal == VARIANT_TRUE);

arrIndex[0] = 4;
SafeArrayPtrOfIndex(pArray, arrIndex, (void **)&pVar);
BOOL bConsolidateAudio = (pVar->boolVal == VARIANT_TRUE);

printf("Ganged: %d Gang Index: %d Single Controller: %d Consolidate Video: %d Consolidate Audio: %d\n\n", 
 bGanged, nGangIndex, bSingleController, bConsolidateVideo, bConsolidateAudio);

hr = spController->CloseChannel();
}}}
When you get an instance of a ~MediaMgr, it keeps track of event notifications (i.e. changes in assets, bins, edits, etc.). Calling ~MediaMgr's ~GetXmlChanges returns an XML document of all notifications since the last time you called it. The next time ~GetXmlChanges is called, only new notifications will be returned. Note that each ~MediaMgr object keeps track of its own list of notifications.

Currently, the best way for getting event notifications is to call ~GetXmlChanges on a regular interval (say, every 0.5 second). 

The different event types returned by ~GetXmlChanges can be found here: [[K2EventTypes.xml|files/K2EventTypes.xml]]

For example, let's say you want to keep track of all new clips or changes to clips. Calling ~GetXmlChanges and querying for notices with ~EventType="~MovieContent" returns this information plus all attributes of the clips. This is an efficient way of finding out what has changed rather than querying individual properties of clips.

Here are some code examples of how to call ~GetXmlChanges:
{{{
[C#]
IMediaMgr mediaMgr = iappServer.CreateMediaMgr(appName);

int loopCount = 0;
int changeCount = 0;
string xmlData = "";

while ( loopCount < 10 )
{
 mediaMgr.GetXmlChanges(1000000, ref changeCount, out xmlData);
 if ( xmlData.Length > 0)
 Console.WriteLine(xmlData);

 Thread.Sleep(500);
 loopCount++;
}

mediaMgr.Dispose();
}}}
{{{
[C++]
IMediaMgrPtr spMediaMgr;
hr = spAppServer->CreateMediaMgr(m_bstrAppName, &spMediaMgr);

long count = 0;
BSTR bstrXmlData = NULL; // must set this to NULL!

spMediaMgr->GetXmlChanges(1000000, &count, &bstrXmlData);
}}}
Error codes in the log are usually displayed in a hexadecimal format as indicated by the number starting with a "0x" (e.g. 0x80041000). Any error code that starts with 0x8004 prefix is an error code defined by Grass Valley. All Grass Valley K2 error codes are listed below. 

Other error codes are most often defined by Microsoft. To get more information about [[Microsoft error codes click here|Microsoft error codes]].

Click here for an error code header file: [[K2ErrorCodes.h|files/K2ErrorCodes.h]]

''System errors:''
|''Code''|''Error''|
|0x80041000|~SYS_E_GENERAL_ERROR|
|0x80041001|~SYS_E_FILENOTOPEN|
|0x80041002|~SYS_E_FILEALREADYOPEN|
|0x80041003|~SYS_E_FILE|
|0x80041004|~SYS_E_UNKNOWN_FORMAT|

''Timeline Server errors:''
|''Code''|''Error''|
|0x80043001|~TMSSRV_E_CHANNEL_NOT_ALLOCATED |
|0x80043002|~TMSSRV_E_INVALID_RESOURCE_TYPE |
|0x80043003|~TMSSRV_E_INVALID_CONTEXT_CHANGE |
|0x80043004|~TMSSRV_E_THREAD_STARTUP_FAILED |
|0x80043005|~TMSSRV_E_RIFF_CREATE |
|0x80043006|~TMSSRV_E_RIFF_READ |
|0x80043007|~TMSSRV_E_RIFF_WRITE |
|0x80043008|~TMSSRV_E_RIFF_ASCEND |
|0x80043009|~TMSSRV_E_RIFF_DESCEND |
|0x8004300A|~TMSSRV_E_RIFF_OPEN |
|0x8004300B|~TMSSRV_E_RIFF_MEM_ACCESS |
|0x8004300C|~TMSSRV_E_RIFF_BAD_SIZE |
|0x8004300D|~TMSSRV_E_RIFF_SEEK |
|0x8004300E|~TMSSRV_E_INVALID_RESOURCE_PROPERTY |
|0x8004300F|~TMSSRV_E_INVALID_RESOURCE_OP |
|0x80043010|~TMSSRV_E_UNKNOWN_VOLUME_NAME |
|0x80043011|~TMSSRV_E_IPM_TIMEOUT |
|0x80043012|~TMSSRV_E_RTS_FAILURE |
|0x80043013|~TMSSRV_E_VIDEO_FORMAT |
|0x80043014|~TMSSRV_E_VIDEO_COMPRESSION |
|0x80043015|~TMSSRV_E_RTS_SHMEM |
|0x80043016|~TMSSRV_E_BAD_FILE_RANGE |
|0x80043017|~TMSSRV_E_INVALID_CHAN_PROPERTY |
|0x80043018|~TMSSRV_E_INVALID_CHAN_OP |
|0x80043019|~TMSSRV_E_TOO_CLOSE_TO_RECORD |
|0x8004301A|~TMSSRV_E_CHAN_OFFLINE |
|0x8004301B|~TMSSRV_E_FILE_OPEN_FAILED |
|0x8004301C|~TMSSRV_E_FILE_CREATE_FAILED |
 
''Transfer Queue & Transfer Session errors:''
|''Code''|''Error''|
|0x80044002 |~XFRSRV_E_REMOVE_TRANSFERRING_ITEM |
|0x80044003 |~XFRSRV_E_LOAD_XML |
|0x80044005 |~XFRSRV_E_CREATE_MMF |
|0x80044006 |~XFRSRV_E_MAP_VIEW |
|0x80044007 |~XFRSRV_E_QUEUE_THREAD_START |
|0x80044008 |~XFRSRV_E_ABORT_ITEM |
|0x80044009 |~XFRSRV_E_ITEM_NOT_FOUND |
|0x8004400A |~XFRSRV_E_UPDATE_ABORTED_ITEM |
|0x8004400B |~XFRSRV_E_INVALID_NAME |

''Transfer session & server errors:''
|''Code''|''Error''|
| 0x80044c01 |~E_XFER_NOT_SUPPORTED |
| 0x80044c02 |~E_XFER_ABORTED_NO_PROGRESS |
| 0x80044c03 |~E_NO_SYS_RESOURCES |
| 0x80044c04 |~E_XFER_SESSION_LOAD_ERROR |
| 0x80044c05 |~E_XFER_BAD_UML_FORM |
| 0x80044c06 |~E_XFER_CTL_ABORTED |
| 0x80044f01 |~E_XFER_REACH_QUEUE_CAPACITY |

''Transfer movie data provider errors:''
|''Code''|''Error''|
| 0x80044e01 |~E_MDP_GENERAL_FAILURE |
| 0x80044e02 |~E_MDP_BAD_UML_FORM |
| 0x80044e03 |~E_MDP_MOVIE_EXISTS |
| 0x80044e04 |~E_MDP_BAD_MOVIE |
| 0x80044e05 |~E_MDP_XFER_NOT_DEF |
| 0x80044e06 |~E_MDP_MDS_ERROR |
| 0x80044e07 |~E_MDP_MOVIE_NOT_EXIST |
| 0x80044e08 |~E_MDP_NO_MOVIE_PERMISSION |
| 0x80044e09 |~E_MDP_NO_BIN_PERMISSION |
| 0x80044e0A |~E_MDP_DIRTY_CLIP |

''Transfer filter subsystem errors:''
|''Code''|''Error''|
| 0x80044D01 |~E_FLT_GENERAL_FAILURE |
| 0x80044D02 |~E_FLT_UMF_FORM |
| 0x80044D03 |~E_FLT_BAD_STREAM |
| 0x80044D04 |~E_FLT_BAD_MEDIATYPE |
| 0x80044D05 |~E_FLT_BAD_UML_FORM |
| 0x80044D06 |~E_FLT_BAD_MOVIE |
| 0x80044D07 |~E_FLT_STRM_TOO_SHORT |
| 0x80044D08 |~E_FLT_NET_ERROR |
| 0x80044D09 |~E_FLT_UNSUPPORTED_VIDEO_RESOLUTION |
| 0x80044D0A |~E_FLT_UNSUPPORTED_AUDIO_SAMPLING |
| 0x80044D0B |~E_FLT_UNSUPPORTED_AUDIO_FORMAT |
| 0x80044D0C |~E_FLT_UNSUPPORTED_AUDIO_SAMPLESIZE |
| 0x80044D0D |~E_INPROG_MOVIE_TIMEOUT |
| 0x80044D0E |~E_FLT_STRM_ABORT |
| 0x80044D0F |~E_FLT_SRL_NOT_ALLOWED |
| 0x80044D10 |~E_FLT_OPEN_GOP |
| 0x80044D11 |~E_FLT_BOTTOM_FIELD_FIRST |
| 0x80044D12 |~E_FLT_ILLEGAL_MEDIA_STATE |

''Transfer MXF filter errors:''
|''Code''|''Error''|
| 0x80044b01 |~E_MXF_GENERAL_FAILURE |
| 0x80044b02 |~E_MXF_CONFORMANCE_FAILURE |
| 0x80044b03 |~E_MXF_KLVPARSE_FAILURE |
| 0x80044b04 |~E_MXF_UNSUPPORTED_VID_FORMAT |
| 0x80044b05 |~E_MXF_HDR_CONSISTENCY_FAILURE |
| 0x80044b06 |~E_MXF_RUNINS_NOT_SUPPORTED |
| 0x80044b07 |~E_MXF_UNSUPPORTED_OP_FORMAT |
| 0x80044b08 |~E_MXF_DICTIONARY_LOOKUP_ERROR |
| 0x80044b09 |~E_MXF_MISSING_ESSENCE_DATA |
| 0x80044b0A |~E_MXF_OPEN_HEADER_FAILURE |

''~AppServer errors:''
|''Code''|''Error''|
|0x80045001|~APPSRVDOTNET_E_STOLEN_CHANNEL |
|0x80045002|~APPSRVDOTNET_E_CHANNEL_IN_USE |
|0x80045003|~APPSRVDOTNET_E_SUITE_IN_USE |
|0x80045004|~APPSRVDOTNET_E_APPSERVICE_OFFLINE|

''Movie Server errors:''
|''Code''|''Error''|
|0x80045201|~MOVSRV_E_FIND_END |
|0x80045202|~MOVSRV_E_COPY_MOVIE_COMPLETED |
|0x80045203|~MOVSRV_E_DUPLICATE_NAME |
|0x80045204|~MOVSRV_E_ALREADY_OPEN |
|0x80045211|~MOVSRV_E_NO_TOKENS_AVAILABLE |
|0x80045212|~MOVSRV_E_OBJECT_READONLY |
|0x80045213|~MOVSRV_E_MOVIE_LOCKED |
|0x80045214|~MOVSRV_E_NOT_CONTROLLER |
|0x80045215|~MOVSRV_E_COPY_MOVIE_DENIED |
|0x80045216|~MOVSRV_E_SHARED_MEDIA |
|0x80045217|~MOVSRV_E_MEDIA_FILESYSTEM_FULL |
|0x80045218|~MOVSRV_E_BAD_CMF_FILE_LEN |
|0x80045221|~MOVSRV_E_INVALID_INPUT |
|0x80045222|~MOVSRV_E_BAD_NAME_STRING |
|0x80045223|~MOVSRV_E_STRING_TOO_LONG |
|0x80045224|~MOVSRV_E_BAD_TOKEN |
|0x80045225|~MOVSRV_E_NOT_CLIP |
|0x80045226|~MOVSRV_E_OPERATION_NOT_ALLOWED |
|0x80045231|~MOVSRV_E_SOME_NOTIFICATION_DATA_DISCARDED|
|0x80045232|~MOVSRV_E_INVALID_NOTIFICATION |
|0x80045241|~MOVSRV_E_MUTEX_TIMEOUT |
|0x80045242|~MOVSRV_E_SLOT_TIMEOUT |
|0x80045243|~MOVSRV_E_DB_SEARCH_FAILED |
|0x80045244|~MOVSRV_E_INVALID_DATA |
|0x80045245|~MOVSRV_E_SYSTEM_ERROR |
|0x80045246|~MOVSRV_E_BAD_SLOT |
|0x80045247|~MOVSRV_E_SLOT_LOCK_ERROR |
|0x80045250|~MOVSRV_E_BAD_POINTER_ARGUMENT |
|0x80045251|~MOVSRV_E_MUST_BE_LOCAL_CONNECTION |
|0x80045252|~MOVSRV_E_DB_SERVER_CONNECTION_FAILED |
|0x80045260|~MOVSRV_E_INVALID_SECTION |
|0x80045261|~MOVSRV_E_INVALID_EDIT |
|0x80045262|~MOVSRV_E_INVALID_SECTION_PROPERTY |
|0x80045263|~MOVSRV_E_INVALID_EDIT_PROPERTY |
|0x80045264|~MOVSRV_E_INVALID_SECTION_OP |
|0x80045265|~MOVSRV_E_BAD_URI_STRING |
|0x80045266|~MOVSRV_E_BAD_URI_TYPE |
|0x80045267|~MOVSRV_E_INVALID_ASSET_OP |
|0x80045268|~MOVSRV_E_INVALID_CTX_CHANGE |
|0x80045269|~MOVSRV_E_BAD_SECTION_TIMECODE |
|0x8004526A|~MOVSRV_E_NO_TRACKS |
|0x8004526B|~MOVSRV_E_MIN_SECTION_LIMIT |
|0x8004526C|~MOVSRV_E_MAX_SECTION_LIMIT |
|0x8004526D|~MOVSRV_E_MAX_EDIT_LIMIT |
|0x8004526E|~MOVSRV_E_MIN_FIELDS_LIMIT |
|0x8004526F|~MOVSRV_E_PERMISSION_DENIED |
|0x80045270|~MOVSRV_E_TIMECODE_NOT_FOUND |
 
''Configuration Server errors:''
|''Code''|''Error''|
|0x80046201|~CFGSRV_E_DATABASE_CONNECTION |
|0x80046202|~CFGSRV_E_RTS_CONNECTION |
|0x80046203|~CFGSRV_E_REMOTEHOST_NOT_EXIST |
|0x80046204|~CFGSRV_E_RTS_IPM_CONNECT_ERROR |
|0x80046205|~CFGSRV_E_RTS_IPM_THREAD_STARTUP |
|0x80046206|~CFGSRV_E_RTS_IPM_THREAD_SHUTDOWN|
|0x80046207|~CFGSRV_E_RTS_IPM_THREAD_TIMEOUT |
|0x80046208|~CFGSRV_E_RTS_INVALID_DATA |
|0x80046209|~CFGSRV_E_UNKNOWN_BOARD_TYPE |
|0x8004620A|~CFGSRV_E_COMMIT_IN_PROGRESS |
|0x8004620B|~CFGSRV_E_FILE_WRITE_FAILURE |

''Channel Status errors:''
|''Code''|''Error''|
|0x80048001|~CHSRV_E_STRING_TOO_LONG |
|0x80048002|~CHSRV_E_BAD_TEXT_INDEX |
|0x80048003|~CHSRV_E_THUMBNAIL_TOO_BIG |
|0x80048004|~CHSRV_E_EMPTY_STATUS_QUERY |
|0x80048005|~CHSRV_E_UNKNOWN_STATUS_QUERY|
|0x80048006|~CHSRV_E_UNKNOWN_CHAN_NAME |
|0x80048007|~CHSRV_E_MMF_MUTEX_TIMEOUT |
 
''~MediaMgr Server errors:''
|''Code''|''Error''|
|0x80049002|~XFRSRV_E_REMOVE_TRANSFERRING_ITEM|
|0x80049003|~XFRSRV_E_LOAD_XML |
|0x80049005|~XFRSRV_E_CREATE_MMF |
|0x80049006|~XFRSRV_E_MAP_VIEW |
|0x80049007|~XFRSRV_E_QUEUE_THREAD_START |
|0x80049008|~XFRSRV_E_ABORT_ITEM |
|0x80049009|~XFRSRV_E_ITEM_NOT_FOUND |
|0x8004900A|~XFRSRV_E_UPDATE_ABORTED_ITEM |
|0x8004900B|~XFRSRV_E_INVALID_NAME |

''Control Server errors:''
|''Code''|''Error''|
|0x8004a001|~CTLSRV_E_NOT_CONNECTED |
|0x8004a002|~CTLSRV_E_NOT_OPEN |
|0x8004a003|~CTLSRV_E_NO_CLIP_LOADED |
|0x8004a004|~CTLSRV_E_NO_UNIQUE_NAME |
|0x8004a005|~CTLSRV_E_UNKNOWN_URI_FORMAT |
|0x8004a006|~CTLSRV_E_NEGATIVE_OFFSET |
|0x8004a007|~CTLSRV_E_UNKNOWN_CTX_TRIGGER |
|0x8004a008|~CTLSRV_E_CHAN_TYPE |
|0x8004a009|~CTLSRV_E_NO_PORT_CONTEXT |
|0x8004a00A|~CTLSRV_E_RTS_SHMEM |
|0x8004a00B|~CTLSRV_E_UNKNOWN_CTX_CONDITION|
|0x8004a00C|~CTLSRV_E_TIMECODE_NOT_FOUND |
|0x8004a00D|~CTLSRV_E_BAD_CTX_CHANGE |

''~AppCenter errors:''
|''Code''|''Error''|
|0x8004b001|~FRMWK_E_NOT_OPEN |
|0x8004b002|~FRMWK_E_STARTUP |
|0x8004b003|~FRMWK_E_ALREADY_OPEN|

''Quality of Service Manager errors:''
|''Code''|''Error''|
| 0x8004d201 |~QOSSRV_E_STRING_TOO_LONG |
| 0x8004d202 |~QOSSRV_E_MMF_MUTEX_TIMEOUT |
| 0x8004d203 |~QOSSRV_E_CLIENT_EXISTS |
| 0x8004d204 |~QOSSRV_E_CLIENT_NOT_FOUND |
| 0x8004d205 |~QOSSRV_E_CLIENTS_FULL |
| 0x8004d206 |~QOSSRV_E_INVALID_CLIENT_INDEX |
| 0x8004d207 |~QOSSRV_E_VOLQOSACCESS_NOT_FOUND |
| 0x8004d208 |~QOSSRV_E_QOS_NOT_SUPPORTED |

''Error chain context errors''
|''Code''|''Error''|
|0x8004ffxx|These are error messages that are used to chain other error messages together. They are used internally for creating a stack trace of all calls in an error. |
If you have an application where you need to use an object on a regular basis, create the object once and hang onto it. Don't repeatedly create objects and then throw them away.

Here's an example:

Let's say that you have a routine that is regularly called. This routine checks for clips in a bin. The routine runs every 30 seconds. In this routine you use a ~MediaMgr object to make ~CheckAsset calls. Rather than creating a new ~MediaMgr object each time the routine is called, create the MediaMgr object once and store it somewhere in your program. 

This will eliminate excessive setup and teardown of objects both in the client application and the K2.
!version 1.09 May, 2008
* added [[troubleshooting tips|Troubleshooting and gathering useful logs]]
!version 1.08 Apr, 2008
* added information about [[tstream and checking GXF files|tstream - GXF file parser and checker]]
* added a list of [[Change Notices]] to the menu on the left
* updated the [[K2 Emulator]]. Fixed UI flickering between play and record panels
!version 1.07 March, 2008
* updated the [[K2 Emulator]]. Fixed HD config files and corrected working bin location
* added how to get & set [[Asset user data]]
!version 1.0.6 February, 2008
* added protocol and ~AppServer developer guide ~PDFs to [[Introduction]] section
* [[Test Clips]] are also available. Useful for importing into emulator.
!version 1.0.5 January, 2008
* added answers to questions from [[Nov 2007 Developer Training conference|Developer Training questions - Nov 2007]]
* added [[BVW commands and responses]]
!version 1.0.4 December, 2007
* added subclip examples to [[C# Samples|CS-Script Sample Scripts]] section.
* added [[K2 Emulator]] and [[Test Clips]]
!version 1.0.3 November, 2007
* added [[Exporting and viewing K2 logs]]
!version 1.0.2 October, 2007
* added a video on ''K2 Logging'' to the [[Videos]] section
* added a video on ''C# Scripts'' to the [[Videos]] section
* updated the [[Examples]] section
* added a video on the ''~AppServer API'' to the [[Videos]] section
!version 1.0.1 September, 2007
* added an example on [[Getting and setting Channel Ganging properties]]
* updated information and scripts on the [[C# Script page|CS-Script Sample Scripts]]
!version 1.0.0 August, 2007
* added [[How to turn on function logging to see the calls AppCenter is making]]
* added [[K2 Clip Name Restrictions]]
* added information on [[Protocol Timecode on K2]]
* added [[ControlPoint PC System Requirements]]
* [[K2 3.2 software is released]]
!version 0.1.7 May, 2007
* improved [[error code information|Grass Valley K2 error codes]]
* added better [[tutorial info|Tutorial]]
* added [[playlist example|Playlists sections and events]]
* added more [[file transfer examples|Transfer clips: importing and exporting various file types]]
!version 0.1.6 April, 2007
* added [[error code information|Microsoft error codes]]
* added more [[examples|Examples]]
* a better [[API documentation|K2 API]]
!version 0.1.5 December, 2006
* cleaned up layout and content
* removed many TODO sections
!version 0.1.4 September, 2006
* Added [[channel property info|Channel Status properties]]
* Added [[asset property info|Get asset properties]]
* Added more examples
!version 0.1.3 April, 2006
* Added [[transfer info|Transfer a File]]
!version 0.1.2 March, 2006
* Added suite info
* Added [[sample projects|Sample Projects]]
!version 0.1.1 February 23, 2006
* Filled in some concepts
* Listed some code examples
* Linked example projects
!version 0.1.0 February 21, 2006
* Started K2 ~AppServer Developer's Guide
* Initial goal: list example code so that developers can get started writing code
Below is sample code that shows you how to query for a system's drive letter(s). See why you [[don't want to hardcode the V: drive|Don't hardcode the V: drive]].
{{{
[C#]
// setup a default volume string
string volume = "V:";

// create a MediaMgr object
IMediaMgr mediaMgr = iappServer.CreateMediaMgr(appName);

// get a cookie to an XML list representation of all volumes (drive letters) on the K2
int count = 0;
int cookie = mediaMgr.EnumerateVolumes(ref count);

// loop thru all volume results
int i = 0;
string result = "";
int maxCount = 256;
string xmlData = "";
count = maxCount;
while (count == maxCount)
{
 mediaMgr.GetXmlResults(cookie, i * maxCount, maxCount, ref count, out xmlData);
 result += xmlData;
 i++;
}

// uncomment if you want to see the XML version of the enumerated volumes
// Console.WriteLine(result);

// NOTE: make sure your close the results set when you are finished! This 
// releases a database selector. Each MediaMgr is allowed to use up to ten 
// database selectors. If you don't close them, you will run out of selectors
// and your EnumerateX calls will fail.
mediaMgr.CloseResults(cookie);

// load the XML string into an XML document
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(result);

// need a namespace manager to parse the xml doc
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(xmlDoc.NameTable);
namespaceManager.AddNamespace("ns", "x-schema:#Schema1");

// loop thru all volumes looking for a V: or C: drive letter
XmlNodeList volumeList = xmlDoc.SelectNodes(("./Select/ns:Data/ns:Dataset"), namespaceManager);
if (volumeList.Count > 0)
{
 IEnumerator volumeNodeEnum = volumeList.GetEnumerator();
 bool bMoreVolumes = volumeNodeEnum.MoveNext();

 do
 {
 // look for the "DatasetName" (i.e drive letter) value.
 XmlNode volumeNode = (XmlNode)volumeNodeEnum.Current;
 string volumeName = volumeNode.Attributes.GetNamedItem("DatasetName").Value;

 // if we found a "V:" or "C:" value save it as the volume name
 if ( 0 == String.Compare( volumeName, "V:", true) ||
 0 == String.Compare( volumeName, "C:", true) )
 {
 Console.WriteLine("Discovered drive " + volumeName);
 volume = volumeName;
 break;
 }

 bMoreVolumes = volumeNodeEnum.MoveNext();
 } while (bMoreVolumes);
} // if volumeList.Count > 0
}}}
Please see the updated information in [[Enable function trace logging]].
The sample code below shows how to import tracks from one clip to another. It also shows how to label tracks, delete tracks, and move tracks. Other track functionality can be found in the [[K2 API]] under the ~ITrackEditor interface.

{{{
C#
IMediaMgr mediaMgr = iappServer.CreateMediaMgr(appName);

// define clip to modify and imported WAV clip
string clip = "edl/cmf//local/V:/default/Clip";
string wavClip = "edl/cmf//local/V:/default/wavClip";

// Note: this test app is using an imported clip named "wavClip". If you wanted to import a temporary clip that 
// you did not want to appear in AppCenter's list of clips, you could import a clip and give it a name that starts 
// with a tilde (~). All clips starting with tilde's are treated as temporary clips. They will not appear in AppCenter's
// list of clips and they will automatically be deleted after a little while...

// get editors for both clips
ISimpleEditor wavEditor = (ISimpleEditor) mediaMgr.CreateEditor(wavClip);
ISimpleEditor clipEditor = (ISimpleEditor) mediaMgr.CreateEditor(clip);
ITrackEditor clipTrackEditor = (ITrackEditor) clipEditor;

// get the XML representation of the WAV clip
string wavXml;
wavEditor.GetXml("", out wavXml);
// Console.WriteLine(wavXml);
 
// we're finished with wavEditor, so get rid of the editor and clean up
wavEditor.Detach();
wavEditor.Dispose();


// load the XML string into an XML document so we can parse for Tracks
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(wavXml);

// load the namespace into a namespace manager. used for parsing the xml doc
XmlNamespaceManager namespaceManager = new XmlNamespaceManager(xmlDoc.NameTable);
namespaceManager.AddNamespace("ns", @"PdrDB__1.0");

// loop thru all tracks and look for audio tracks only (i.e TrackType="PdrAudioTrack". Uncomment the above 
// Console.WriteLine(wavXml) to see all XML data including other TrackTypes.)
XmlNodeList trackList = xmlDoc.SelectNodes(("//ns:XML/ns:Data/ns:Movie/ns:Track"), namespaceManager);

// if our clip has tracks, loop thru all tracks
if (trackList.Count > 0)
{
	// setup enumerator for loop
	IEnumerator trackNodeEnum = trackList.GetEnumerator();
	bool bMoreTracks = trackNodeEnum.MoveNext();

	do
	{
		// Get the TrackType, TrackNumber, and TrackId attribues from the Track node
		XmlNode trackNode = (XmlNode)trackNodeEnum.Current;
		string trackType = trackNode.Attributes.GetNamedItem("TrackType").Value;
		int trackNumber = Int32.Parse ( trackNode.Attributes.GetNamedItem("TrackNumber").Value );
		string trackId = trackNode.Attributes.GetNamedItem("TrackId").Value;

		// if we found a "PdrAudioTrack, add it to the Clip
		if ( 0 == String.Compare( trackType, "PdrAudioTrack", true) )
		{
			Console.WriteLine("Track type: {0}  number: {1}  ID: {2}", trackType, trackNumber, trackId);

			// add WAV audio track to Clip
			int addedTrackIndex;
			string addedTrackId;
			
			clipTrackEditor.AddTrack(wavClip, trackId, Int32.MinValue, Int32.MaxValue, 
				0, out addedTrackIndex, out addedTrackId);

			Console.WriteLine("Added track index: {0}  ID: {1}", addedTrackIndex, addedTrackId);
			
			
			// Let's also setup a label for this track. You can see these in AppCenter in clip properties page under the Tracks button.
			string label = "xyz";
			Console.WriteLine("Set track {0} label to '{1}'", addedTrackId, label);
			clipTrackEditor.SetTrackLabel(addedTrackId, label);

			
			/*   uncomment to test
			// delete tracks from Clip
			Console.WriteLine("Delete track {0}", addedTrackId);
			clipTrackEditor.DeleteTrack(addedTrackId);
			*/
			
			/*   uncomment to test
			// move track position in Clip
			int position = 0;
			Console.WriteLine("Move track {0} to position {1}", addedTrackId, position);
			clipTrackEditor.MoveTrack(addedTrackId, position);										
			*/
			
			Console.WriteLine();									
		}

		bMoreTracks = trackNodeEnum.MoveNext();
	} while (bMoreTracks);
	
} // if trackList.Count > 0							

// cleanup - make sure you detach or movie ref will remain open!
clipEditor.Detach();
clipEditor.Dispose();
clipEditor = null;

mediaMgr.Dispose();
}}}
See the [[Install Client Libraries]] section for instructions on how to load and register ~DLLs and get necessary header files for C++ development.

{{{
[C#]
using GrassValley.Mseries.AppServer;
using GrassValley.Mseries.AppServerLog;
using GrassValley.Mseries.ChanStatus;
using GrassValley.Mseries.ConfigMgr;
using GrassValley.Mseries.Control;
using GrassValley.Mseries.DVCapture;
using GrassValley.Mseries.Editor;
using GrassValley.Mseries.MediaMgr;
using GrassValley.Mseries.Security;
using GrassValley.Mseries.Status;
using GrassValley.Mseries.TransferQueue;
}}}

{{{
[C++]
#include "grassvalley.mseries.appserverlog.tlh"
#include "grassvalley.mseries.configmgr.tlh"
#include "grassvalley.mseries.dvcapture.tlh"
#include "grassvalley.mseries.editor.tlh"
#include "grassvalley.mseries.mediamgr.tlh"
#include "grassvalley.mseries.status.tlh"
#include "grassvalley.mseries.transferqueue.tlh"
#include "GrassValley.Mseries.ChanStatus.tlh"
#include "GrassValley.Mseries.Control.tlh"
#include "GrassValley.Mseries.AppServer.tlh"
}}}
/***
|Name|InlineJavascriptPlugin|
|Source|http://www.TiddlyTools.com/#InlineJavascriptPlugin|
|Documentation|http://www.TiddlyTools.com/#InlineJavascriptPluginInfo|
|Version|1.9.3|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|plugin|
|Requires||
|Overrides||
|Description|Insert Javascript executable code directly into your tiddler content.|
''Call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
!!!!!Documentation
>see [[InlineJavascriptPluginInfo]]
!!!!!Revisions
<<<
2008.06.11 [1.9.3] added $(...) function as 'shorthand' convenience syntax for document.getElementById()
2008.03.03 [1.9.2] corrected declaration of wikifyPlainText() for 'TW 2.1.x compatibility fallback' (fixes Safari "parse error")
2008.02.23 [1.9.1] in onclick function, use string instead of array for 'bufferedHTML' attribute on link element (fixes IE errors)
2008.02.21 [1.9.0] 'onclick' scripts now allow returned text (or document.write() calls) to be wikified into a span that immediately follows the onclick link.  Also, added default 'return false' handling if no return value provided (prevents HREF from being triggered -- return TRUE to allow HREF to be processed).  Thanks to Xavier Verges for suggestion and preliminary code.
|please see [[InlineJavascriptPluginInfo]] for additional revision details|
2005.11.08 [1.0.0] initial release
<<<
!!!!!Code
***/
//{{{
version.extensions.inlineJavascript= {major: 1, minor: 9, revision: 3, date: new Date(2008,6,11)};

config.formatters.push( {
	name: "inlineJavascript",
	match: "\\<script",
	lookahead: "\\<script(?: src=\\\"((?:.|\\n)*?)\\\")?(?: label=\\\"((?:.|\\n)*?)\\\")?(?: title=\\\"((?:.|\\n)*?)\\\")?(?: key=\\\"((?:.|\\n)*?)\\\")?( show)?\\>((?:.|\\n)*?)\\</script\\>",

	handler: function(w) {
		var lookaheadRegExp = new RegExp(this.lookahead,"mg");
		lookaheadRegExp.lastIndex = w.matchStart;
		var lookaheadMatch = lookaheadRegExp.exec(w.source)
		if(lookaheadMatch && lookaheadMatch.index == w.matchStart) {
			var src=lookaheadMatch[1];
			var label=lookaheadMatch[2];
			var tip=lookaheadMatch[3];
			var key=lookaheadMatch[4];
			var show=lookaheadMatch[5];
			var code=lookaheadMatch[6];
			if (src) { // load a script library
				// make script tag, set src, add to body to execute, then remove for cleanup
				var script = document.createElement("script"); script.src = src;
				document.body.appendChild(script); document.body.removeChild(script);
			}
			if (code) { // there is script code
				if (show) // show inline script code in tiddler output
					wikify("{{{\n"+lookaheadMatch[0]+"\n}}}\n",w.output);
				if (label) { // create a link to an 'onclick' script
					// add a link, define click handler, save code in link (pass 'place'), set link attributes
					var link=createTiddlyElement(w.output,"a",null,"tiddlyLinkExisting",wikifyPlainText(label));
					var fixup=code.replace(/document.write\s*\(/gi,'place.bufferedHTML+=(');
					link.code="function _out(place){"+fixup+"\n};_out(this);"
					link.tiddler=w.tiddler;
					link.onclick=function(){
						this.bufferedHTML="";
						try{ var r=eval(this.code);
							if(this.bufferedHTML.length || (typeof(r)==="string")&&r.length)
								var s=this.parentNode.insertBefore(document.createElement("span"),this.nextSibling);
							if(this.bufferedHTML.length)
								s.innerHTML=this.bufferedHTML;
							if((typeof(r)==="string")&&r.length) {
								wikify(r,s,null,this.tiddler);
								return false;
							} else return r!==undefined?r:false;
						} catch(e){alert(e.description||e.toString());return false;}
					};
					link.setAttribute("title",tip||"");
					var URIcode='javascript:void(eval(decodeURIComponent(%22(function(){try{';
					URIcode+=encodeURIComponent(encodeURIComponent(code.replace(/\n/g,' ')));
					URIcode+='}catch(e){alert(e.description||e.toString())}})()%22)))';
					link.setAttribute("href",URIcode);
					link.style.cursor="pointer";
					if (key) link.accessKey=key.substr(0,1); // single character only
				}
				else { // run inline script code
					var fixup=code.replace(/document.write\s*\(/gi,'place.innerHTML+=(');
					var code="function _out(place){"+fixup+"\n};_out(w.output);"
					try { var out=eval(code); } catch(e) { out=e.description?e.description:e.toString(); }
					if (out && out.length) wikify(out,w.output,w.highlightRegExp,w.tiddler);
				}
			}
			w.nextMatch = lookaheadMatch.index + lookaheadMatch[0].length;
		}
	}
} )
//}}}

// // Backward-compatibility for TW2.1.x and earlier
//{{{
if (typeof(wikifyPlainText)=="undefined") window.wikifyPlainText=function(text,limit,tiddler) {
	if(limit > 0) text = text.substr(0,limit);
	var wikifier = new Wikifier(text,formatter,null,tiddler);
	return wikifier.wikifyPlain();
}
//}}}

// // $(...) function: 'shorthand' convenience syntax for document.getElementById()
//{{{
if (typeof($)=="undefined") { // avoid redefinition
function $() {
	var elements=new Array();
	for (var i=0; i<arguments.length; i++) {
		var element=arguments[i];
		if (typeof element=='string') element=document.getElementById(element);
		if (arguments.length==1) return element;
		elements.push(element);
	}
	return elements;
}
}
//}}}
/***
|Name|InlineJavascriptPluginInfo|
|Source|http://www.TiddlyTools.com/#InlineJavascriptPlugin|
|Documentation|http://www.TiddlyTools.com/#InlineJavascriptPluginInfo|
|Version|1.9.3|
|Author|Eric Shulman - ELS Design Studios|
|License|http://www.TiddlyTools.com/#LegalStatements <br>and [[Creative Commons Attribution-ShareAlike 2.5 License|http://creativecommons.org/licenses/by-sa/2.5/]]|
|~CoreVersion|2.1|
|Type|documentation|
|Requires||
|Overrides||
|Description|Documentation for InlineJavascriptPlugin|
''Call directly into TW core utility routines, define new functions, calculate values, add dynamically-generated TiddlyWiki-formatted output'' into tiddler content, or perform any other programmatic actions each time the tiddler is rendered.
!!!!!Usage
<<<
This plugin adds wiki syntax for surrounding tiddler content with {{{<script>}}} and {{{</script>}}} markers, so that it can be recognized as embedded javascript code.
<script show>
	/* javascript code goes here... */
</script>Every time the tiddler content is rendered, the javascript code is automatically evaluated, allowing you to invoke 'side-effect' processing and/or produce dynamically-generated content that is then inserted into the tiddler content, immediately following the script (see below).  By including the optional ''show'' keyword as the final parameter in a {{{<script>}}} marker, the plugin will also include the script source code in the output that it displays in the tiddler.  This is helpful when creating examples for documentation purposes (such as used in this tiddler!)

__''Deferred execution from an 'onClick' link''__
<script label="click here" title="mouseover tooltip text" key="X" show>
	/* javascript code goes here... */
	alert('you clicked on the link!');
</script>
By including a {{{label="..."}}} parameter in the initial {{{<script>}}} marker, the plugin will create a link to an 'onclick' script that will only be executed when that specific link is clicked, rather than running the script each time the tiddler is rendered.  You may also include a {{{title="..."}}} parameter to specify the 'tooltip' text that will appear whenever the mouse is moved over the onClick link text, and a {{{key="X"}}} parameter to specify an //access key// (which must be a //single// letter or numeric digit only).

__''Loading scripts from external source files''__
<script src="URL" show>
	/* optional javascript code goes here... */
</script>You can also load javascript directly from an external source URL, by including a src="..." parameter in the initial {{{<script>}}} marker (e.g., {{{<script src="demo.js"></script>}}}).  This is particularly useful when incorporating third-party javascript libraries for use in custom extensions and plugins.  The 'foreign' javascript code remains isolated in a separate file that can be easily replaced whenever an updated library file becomes available.

In addition to loading the javascript from the external file, you can also use this feature to invoke javascript code contained within the {{{<script>...</script>}}} markers.  This code is invoked //after// the external script file has been processed, and can make immediate use of the functions and/or global variables defined by the external script file.
>Note: To ensure that your javascript functions are always available when needed, you should load the libraries from a tiddler that is rendered as soon as your TiddlyWiki document is opened, such as MainMenu.  For example: put your {{{<script src="..."></script>}}} syntax into a separate 'library' tiddler (e.g., LoadScripts), and then add {{{<<tiddler LoadScripts>>}}} to MainMenu so that the library is loaded before any other tiddlers that rely upon the functions it defines. 
>
>Normally, loading external javascript in this way does not produce any direct output, and should not have any impact on the appearance of your MainMenu.  However, if your LoadScripts tiddler contains notes or other visible content, you can suppress this output by using 'inline CSS' in the MainMenu, like this: {{{@@display:none;<<tiddler LoadScripts>>@@}}}
<<<
!!!!!Creating dynamic tiddler content and accessing the ~TiddlyWiki DOM
<<<
An important difference between TiddlyWiki inline scripting and conventional embedded javascript techniques for web pages is the method used to produce output that is dynamically inserted into the document: in a typical web document, you use the {{{document.write()}}} (or {{{document.writeln()}}}) function to output text sequences (often containing HTML tags) that are then rendered when the entire document is first loaded into the browser window.

However, in a ~TiddlyWiki document, tiddlers (and other DOM elements) are created, deleted, and rendered "on-the-fly", so writing directly to the global 'document' object does not produce the results you want (i.e., replacing the embedded script within the tiddler content), and instead will //completely replace the entire ~TiddlyWiki document in your browser window (which is clearly not a good thing!)//.  In order to allow scripts to use {{{document.write()}}}, the plugin automatically converts and buffers all HTML output so it can be safely inserted into your tiddler content, immediately following the script.

''Note that {{{document.write()}}} can only be used to output "pure HTML" syntax.  To produce //wiki-formatted// output, your script should instead return a text value containing the desired wiki-syntax content'', which will then be automatically rendered immediately following the script.  If returning a text value is not sufficient for your needs, the plugin also provides an automatically-defined variable, 'place', that gives the script code ''direct access to the //containing DOM element//'' into which the tiddler output is being rendered.  You can use this variable to ''perform direct DOM manipulations'' that can, for example:
* generate wiki-formatted output using {{{wikify("...content...",place)}}}
* vary the script's actions based upon the DOM element in which it is embedded
* access 'tiddler-relative' DOM information using {{{story.findContainingTiddler(place)}}}
Note:
''When using an 'onclick' script, the 'place' element actually refers to the onclick //link text// itself, instead of the containing DOM element.''  This permits you to directly reference or modify the link text to reflect any 'stateful' conditions that might set by the script.  To refer to the containing DOM element from within an 'onclick' script, you can use "place.parentNode" instead.
<<<
!!!!!Instant "bookmarklets"
<<<
You can also use an 'onclick' link to define a "bookmarklet": a small piece of javascript that can be ''invoked directly from the browser without having to be defined within the current document.''  This allows you to create 'stand-alone' commands that can be applied to virtually ANY TiddlyWiki document... even remotely-hosted documents that have been written by others!!  To create a bookmarklet, simply define an 'onclick' script and then grab the resulting link text and drag-and-drop it onto your browser's toolbar (or right-click and use the 'bookmark this link' command to add it to the browser's menu).

Notes:
*When writing scripts intended for use as bookmarklets, due to the ~URI-encoding required by the browser, ''you cannot not use ANY double-quotes (") within the bookmarklet script code.''
*All comments embedded in the bookmarklet script must ''use the fully-delimited {{{/* ... */}}} comment syntax,'' rather than the shorter {{{//}}} comment syntax.
*Most importantly, because bookmarklets are invoked directly from the browser interface and are not embedded within the TiddlyWiki document, there is NO containing 'place' DOM element surrounding the script.  As a result, ''you cannot use a bookmarklet to generate dynamic output in your document,''  and using {{{document.write()}}} or returning wiki-syntax text or making reference to the 'place' DOM element will halt the script and report a "Reference Error" when that bookmarklet is invoked.  
Please see [[InstantBookmarklets]] for many examples of 'onclick' scripts that can also be used as bookmarklets.
<<<
!!!!!Special reserved function name
<<<
The plugin 'wraps' all inline javascript code inside a function, {{{_out()}}}, so that any return value you provide can be correctly handled by the plugin and inserted into the tiddler.  To avoid unpredictable results (and possibly fatal execution errors), this function should never be redefined or called from ''within'' your script code.
<<<
!!!!!$(...) 'shorthand' function
<<<
As described by Dustin Diaz [[here|http://www.dustindiaz.com/top-ten-javascript/]], the plugin defines a 'shorthand' function that allows you to write:
{{{
$(id)
}}}
in place of the normal standard javascript syntax:
{{{
document.getElementById(id)
}}}
This function is provided merely as a convenience for javascript coders that may be familiar with this abbreviation, in order to allow them to save a few bytes when writing their own inline script code.
<<<
!!!!!Examples
<<<
simple dynamic output:
><script show>
	document.write("The current date/time is: "+(new Date())+"<br>");
	return "link to current user: [["+config.options.txtUserName+"]]\n";
</script>
dynamic output using 'place' to get size information for current tiddler:
><script show>
	if (!window.story) window.story=window;
	var title=story.findContainingTiddler(place).getAttribute("tiddler");
	var size=store.getTiddlerText(title).length;
	return title+" is using "+size+" bytes";
</script>
dynamic output from an 'onclick' script, using {{{document.write()}}} and/or {{{return "..."}}}
><script label="click here" show>
	document.write("<br>The current date/time is: "+(new Date())+"<br>");
	return "link to current user: [["+config.options.txtUserName+"]]\n";
</script>
creating an 'onclick' button/link that accesses the link text AND the containing tiddler:
><script label="click here" title="clicking this link will show an 'alert' box" key="H" show>
	if (!window.story) window.story=window;
	var txt=place.firstChild.data;
	var tid=story.findContainingTiddler(place).getAttribute('tiddler');
	alert('Hello World!\nlinktext='+txt+'\ntiddler='+tid);
</script>
dynamically setting onclick link text based on stateful information:
>{{block{
{{{
<script label="click here">
	/* toggle "txtSomething" value */
	var on=(config.txtSomething=="ON");
	place.innerHTML=on?"enable":"disable";
	config.txtSomething=on?"OFF":"ON";
	return "\nThe current value is: "+config.txtSomething;
</script><script>
	/* initialize onclick link text based on current "txtSomething" value */
	var on=(config.txtSomething=="ON");
	place.lastChild.previousSibling.innerHTML=on?"disable":"enable";
</script>
}}}
<script label="click here">
	/* toggle "txtSomething" value */
	var on=(config.txtSomething=="ON");
	place.innerHTML=on?"enable":"disable";
	config.txtSomething=on?"OFF":"ON";
	return "\nThe current value is: "+config.txtSomething;
</script><script>
	/* initialize onclick link text based on current "txtSomething" value */
	var on=(config.txtSomething=="ON");
	place.lastChild.innerHTML=on?"enable":"disable";
</script>
}}}
loading a script from a source url:
>http://www.TiddlyTools.com/demo.js contains:
>>{{{function inlineJavascriptDemo() { alert('Hello from demo.js!!') } }}}
>>{{{displayMessage('InlineJavascriptPlugin: demo.js has been loaded');}}}
>note: When using this example on your local system, you will need to download the external script file from the above URL and install it into the same directory as your document.
>
><script src="demo.js" show>
	return "inlineJavascriptDemo() function has been defined"
</script>
><script label="click to invoke inlineJavascriptDemo()" key="D" show>
	inlineJavascriptDemo();
</script>
<<<
!!!!!Revisions
<<<
2008.06.11 [1.9.3] added $(...) function as 'shorthand' convenience syntax for document.getElementById()
2008.03.03 [1.9.2] corrected declaration of wikifyPlainText() for 'TW 2.1.x compatibility fallback' (fixes Safari "parse error")
2008.02.23 [1.9.1] in onclick function, use string instead of array for 'bufferedHTML' attribute on link element (fixes IE errors)
2008.02.21 [1.9.0] 'onclick' scripts now allow returned text (or document.write() calls) to be wikified into a span that immediately follows the onclick link.  Also, added default 'return false' handling if no return value provided (prevents HREF from being triggered -- return TRUE to allow HREF to be processed).  Thanks to Xavier Verges for suggestion and preliminary code.
2008.02.14 [1.8.1] added backward-compatibility for use of wikifyPlainText() in TW2.1.3 and earlier
2008.01.08 [*.*.*] plugin size reduction: documentation moved to ...Info tiddler
2007.12.28 [1.8.0] added support for key="X" syntax to specify custom access key definitions
2007.12.15 [1.7.0] autogenerate URI encoded HREF on links for onclick scripts.  Drag links to browser toolbar to create bookmarklets.  IMPORTANT NOTE: place is NOT defined when scripts are used as bookmarklets.  In addition, double-quotes will cause syntax errors.  Thanks to PaulReiber for debugging and brainstorming.
2007.11.26 [1.6.2] when converting "document.write()" function calls in inline code, allow whitespace between "write" and "(" so that "document.write ( foobar )" is properly converted.
2007.11.16 [1.6.1] when rendering "onclick scripts", pass label text through wikifyPlainText() to parse any embedded wiki-syntax to enable use of HTML entities or even TW macros to generate dynamic label text.
2007.02.19 [1.6.0] added support for title="..." to specify mouseover tooltip when using an onclick (label="...") script
2006.10.16 [1.5.2] add newline before closing '}' in 'function out_' wrapper.  Fixes error caused when last line of script is a comment.
2006.06.01 [1.5.1] when calling wikify() on script return value, pass hightlightRegExp and tiddler params so macros that rely on these values can render properly
2006.04.19 [1.5.0] added 'show' parameter to force display of javascript source code in tiddler output
2006.01.05 [1.4.0] added support 'onclick' scripts.  When label="..." param is present, a button/link is created using the indicated label text, and the script is only executed when the button/link is clicked.  'place' value is set to match the clicked button/link element.
2005.12.13 [1.3.1] when catching eval error in IE, e.description contains the error text, instead of e.toString().  Fixed error reporting so IE shows the correct response text.  Based on a suggestion by UdoBorkowski
2005.11.09 [1.3.0] for 'inline' scripts (i.e., not scripts loaded with src="..."), automatically replace calls to 'document.write()' with 'place.innerHTML+=' so script output is directed into tiddler content.  Based on a suggestion by BradleyMeck
2005.11.08 [1.2.0] handle loading of javascript from an external URL via src="..." syntax
2005.11.08 [1.1.0] pass 'place' param into scripts to provide direct DOM access 
2005.11.08 [1.0.0] initial release
<<<
We are reworking our client library installation. For now download the [[ControlPoint|zip/3221b746_K2ControlPoint.exe]] installer. Unzip the file to a temporary directory and run setup.exe. This will install and register all necessary ~DLLs in the "C:\profile" directory.

Additionally, for C++ development, the necessary K2 API *.tlh files are not included in the ~ControlPoint installer. Download the these [[K2 TLH files|zip/K2TLHFiles.zip]] and unzip them to the C:\profile directory as well.

This will give you all libraries, headers, and registration that you will need to develop software using the K2 ~AppServer API.

See the [[Include Files]] section for examples of C++ "include" statements and C# "using" statements in your code.

You can download C# and C++ sample projects from the [[Sample Projects]] section as well.
/***
|''Name:''|IntelliTaggerPlugin|
|''Version:''|1.0.2 (2007-07-25)|
|''Type:''|plugin|
|''Source:''|http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin|
|''Author:''|Udo Borkowski (ub [at] abego-software [dot] de)|
|''Documentation:''|[[IntelliTaggerPlugin Documentation]]|
|''~SourceCode:''|[[IntelliTaggerPlugin SourceCode]]|
|''Licence:''|[[BSD open source license (abego Software)]]|
|''~CoreVersion:''|2.0.8|
|''Browser:''|Firefox 1.5.0.2 or better|
***/
/***
!Version History
* 1.0.2 (2007-07-25): 
** Feature: "Return" key may be used to accept first tag suggestion (beside "Alt-1")
** Bugfix: Keyboard shortcuts (Alt+3 etc.) shifted
* 1.0.1 (2007-05-18): Improvement: Speedup when using TiddlyWikis with many tags
* 1.0.0 (2006-04-26): Initial release

***/
// /%
if(!version.extensions.IntelliTaggerPlugin){if(!window.abego){window.abego={};}if(!abego.internal){abego.internal={};}abego.alertAndThrow=function(s){alert(s);throw s;};if(version.major<2){abego.alertAndThrow("Use TiddlyWiki 2.0.8 or better to run the IntelliTagger Plugin.");}version.extensions.IntelliTaggerPlugin={major:1,minor:0,revision:2,date:new Date(2007,6,25),type:"plugin",source:"http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin",documentation:"[[IntelliTaggerPlugin Documentation]]",sourcecode:"[[IntelliTaggerPlugin SourceCode]]",author:"Udo Borkowski (ub [at] abego-software [dot] de)",licence:"[[BSD open source license (abego Software)]]",tiddlywiki:"Version 2.0.8 or better",browser:"Firefox 1.5.0.2 or better"};abego.createEllipsis=function(_2){var e=createTiddlyElement(_2,"span");e.innerHTML="&hellip;";};abego.isPopupOpen=function(_4){return _4&&_4.parentNode==document.body;};abego.openAsPopup=function(_5){if(_5.parentNode!=document.body){document.body.appendChild(_5);}};abego.closePopup=function(_6){if(abego.isPopupOpen(_6)){document.body.removeChild(_6);}};abego.getWindowRect=function(){return {left:findScrollX(),top:findScrollY(),height:findWindowHeight(),width:findWindowWidth()};};abego.moveElement=function(_7,_8,_9){_7.style.left=_8+"px";_7.style.top=_9+"px";};abego.centerOnWindow=function(_a){if(_a.style.position!="absolute"){throw "abego.centerOnWindow: element must have absolute position";}var _b=abego.getWindowRect();abego.moveElement(_a,_b.left+(_b.width-_a.offsetWidth)/2,_b.top+(_b.height-_a.offsetHeight)/2);};abego.isDescendantOrSelf=function(_c,e){while(e){if(_c==e){return true;}e=e.parentNode;}return false;};abego.toSet=function(_e){var _f={};for(var i=0;i<_e.length;i++){_f[_e[i]]=true;}return _f;};abego.filterStrings=function(_11,_12,_13){var _14=[];for(var i=0;i<_11.length&&(_13===undefined||_14.length<_13);i++){var s=_11[i];if(s.match(_12)){_14.push(s);}}return _14;};abego.arraysAreEqual=function(a,b){if(!a){return !b;}if(!b){return false;}var n=a.length;if(n!=b.length){return false;}for(var i=0;i<n;i++){if(a[i]!=b[i]){return false;}}return true;};abego.moveBelowAndClip=function(_1b,_1c){if(!_1c){return;}var _1d=findPosX(_1c);var _1e=findPosY(_1c);var _1f=_1c.offsetHeight;var _20=_1d;var _21=_1e+_1f;var _22=findWindowWidth();if(_22<_1b.offsetWidth){_1b.style.width=(_22-100)+"px";}var _23=_1b.offsetWidth;if(_20+_23>_22){_20=_22-_23-30;}if(_20<0){_20=0;}_1b.style.left=_20+"px";_1b.style.top=_21+"px";_1b.style.display="block";};abego.compareStrings=function(a,b){return (a==b)?0:(a<b)?-1:1;};abego.sortIgnoreCase=function(arr){var _27=[];var n=arr.length;for(var i=0;i<n;i++){var s=arr[i];_27.push([s.toString().toLowerCase(),s]);}_27.sort(function(a,b){return (a[0]==b[0])?0:(a[0]<b[0])?-1:1;});for(i=0;i<n;i++){arr[i]=_27[i][1];}};abego.getTiddlerField=function(_2d,_2e,_2f){var _30=document.getElementById(_2d.idPrefix+_2e);var e=null;if(_30!=null){var _32=_30.getElementsByTagName("*");for(var t=0;t<_32.length;t++){var c=_32[t];if(c.tagName.toLowerCase()=="input"||c.tagName.toLowerCase()=="textarea"){if(!e){e=c;}if(c.getAttribute("edit")==_2f){e=c;}}}}return e;};abego.setRange=function(_35,_36,end){if(_35.setSelectionRange){_35.setSelectionRange(_36,end);var max=0+_35.scrollHeight;var len=_35.textLength;var top=max*_36/len,bot=max*end/len;_35.scrollTop=Math.min(top,(bot+top-_35.clientHeight)/2);}else{if(_35.createTextRange!=undefined){var _3b=_35.createTextRange();_3b.collapse();_3b.moveEnd("character",end);_3b.moveStart("character",_36);_3b.select();}else{_35.select();}}};abego.internal.TagManager=function(){var _3c=null;var _3d=function(){if(_3c){return;}_3c={};store.forEachTiddler(function(_3e,_3f){for(var i=0;i<_3f.tags.length;i++){var tag=_3f.tags[i];var _42=_3c[tag];if(!_42){_42=_3c[tag]={count:0,tiddlers:{}};}_42.tiddlers[_3f.title]=true;_42.count+=1;}});};var _43=TiddlyWiki.prototype.saveTiddler;TiddlyWiki.prototype.saveTiddler=function(_44,_45,_46,_47,_48,_49){var _4a=this.fetchTiddler(_44);var _4b=_4a?_4a.tags:[];var _4c=(typeof _49=="string")?_49.readBracketedList():_49;_43.apply(this,arguments);if(!abego.arraysAreEqual(_4b,_4c)){abego.internal.getTagManager().reset();}};var _4d=TiddlyWiki.prototype.removeTiddler;TiddlyWiki.prototype.removeTiddler=function(_4e){var _4f=this.fetchTiddler(_4e);var _50=_4f&&_4f.tags.length>0;_4d.apply(this,arguments);if(_50){abego.internal.getTagManager().reset();}};this.reset=function(){_3c=null;};this.getTiddlersWithTag=function(tag){_3d();var _52=_3c[tag];return _52?_52.tiddlers:null;};this.getAllTags=function(_53){_3d();var _54=[];for(var i in _3c){_54.push(i);}for(i=0;_53&&i<_53.length;i++){_54.pushUnique(_53[i],true);}abego.sortIgnoreCase(_54);return _54;};this.getTagInfos=function(){_3d();var _56=[];for(var _57 in _3c){_56.push([_57,_3c[_57]]);}return _56;};var _58=function(a,b){var a1=a[1];var b1=b[1];var d=b[1].count-a[1].count;return d!=0?d:abego.compareStrings(a[0].toLowerCase(),b[0].toLowerCase());};this.getSortedTagInfos=function(){_3d();var _5e=this.getTagInfos();_5e.sort(_58);return _5e;};this.getPartnerRankedTags=function(_5f){var _60={};for(var i=0;i<_5f.length;i++){var _62=this.getTiddlersWithTag(_5f[i]);for(var _63 in _62){var _64=store.getTiddler(_63);if(!(_64 instanceof Tiddler)){continue;}for(var j=0;j<_64.tags.length;j++){var tag=_64.tags[j];var c=_60[tag];_60[tag]=c?c+1:1;}}}var _68=abego.toSet(_5f);var _69=[];for(var n in _60){if(!_68[n]){_69.push(n);}}_69.sort(function(a,b){var d=_60[b]-_60[a];return d!=0?d:abego.compareStrings(a.toLowerCase(),b.toLowerCase());});return _69;};};abego.internal.getTagManager=function(){if(!abego.internal.gTagManager){abego.internal.gTagManager=new abego.internal.TagManager();}return abego.internal.gTagManager;};(function(){var _6e=2;var _6f=1;var _70=30;var _71;var _72;var _73;var _74;var _75;var _76;if(!abego.IntelliTagger){abego.IntelliTagger={};}var _77=function(){return _72;};var _78=function(tag){return _75[tag];};var _7a=function(s){var i=s.lastIndexOf(" ");return (i>=0)?s.substr(0,i):"";};var _7d=function(_7e){var s=_7e.value;var len=s.length;return (len>0&&s[len-1]!=" ");};var _81=function(_82){var s=_82.value;var len=s.length;if(len>0&&s[len-1]!=" "){_82.value+=" ";}};var _85=function(tag,_87,_88){if(_7d(_87)){_87.value=_7a(_87.value);}story.setTiddlerTag(_88.title,tag,0);_81(_87);abego.IntelliTagger.assistTagging(_87,_88);};var _89=function(n){if(_76&&_76.length>n){return _76[n];}return (_74&&_74.length>n)?_74[n]:null;};var _8b=function(n,_8d,_8e){var _8f=_89(n);if(_8f){_85(_8f,_8d,_8e);}};var _90=function(_91){var pos=_91.value.lastIndexOf(" ");var _93=(pos>=0)?_91.value.substr(++pos,_91.value.length):_91.value;return new RegExp(_93.escapeRegExp(),"i");};var _94=function(_95,_96){var _97=0;for(var i=0;i<_95.length;i++){if(_96[_95[i]]){_97++;}}return _97;};var _99=function(_9a,_9b,_9c){var _9d=1;var c=_9a[_9b];for(var i=_9b+1;i<_9a.length;i++){if(_9a[i][1].count==c){if(_9a[i][0].match(_9c)){_9d++;}}else{break;}}return _9d;};var _a0=function(_a1,_a2){var _a3=abego.internal.getTagManager().getSortedTagInfos();var _a4=[];var _a5=0;for(var i=0;i<_a3.length;i++){var c=_a3[i][1].count;if(c!=_a5){if(_a2&&(_a4.length+_99(_a3,i,_a1)>_a2)){break;}_a5=c;}if(c==1){break;}var s=_a3[i][0];if(s.match(_a1)){_a4.push(s);}}return _a4;};var _a9=function(_aa,_ab){return abego.filterStrings(abego.internal.getTagManager().getAllTags(_ab),_aa);};var _ac=function(){if(!_71){return;}var _ad=store.getTiddlerText("IntelliTaggerMainTemplate");if(!_ad){_ad="<b>Tiddler IntelliTaggerMainTemplate not found</b>";}_71.innerHTML=_ad;applyHtmlMacros(_71,null);refreshElements(_71,null);};var _ae=function(e){if(!e){var e=window.event;}var tag=this.getAttribute("tag");if(_73){_73.call(this,tag,e);}return false;};var _b2=function(_b3){createTiddlyElement(_b3,"span",null,"tagSeparator"," | ");};var _b4=function(_b5,_b6,_b7,_b8,_b9){if(!_b6){return;}var _ba=_b8?abego.toSet(_b8):{};var n=_b6.length;var c=0;for(var i=0;i<n;i++){var tag=_b6[i];if(_ba[tag]){continue;}if(c>0){_b2(_b5);}if(_b9&&c>=_b9){abego.createEllipsis(_b5);break;}c++;var _bf="";var _c0=_b5;if(_b7<10){_c0=createTiddlyElement(_b5,"span",null,"numberedSuggestion");_b7++;var key=_b7<10?""+(_b7):"0";createTiddlyElement(_c0,"span",null,"suggestionNumber",key+") ");var _c2=_b7==1?"Return or ":"";_bf=" (Shortcut: %1Alt-%0)".format([key,_c2]);}var _c3=config.views.wikified.tag.tooltip.format([tag]);var _c4=(_78(tag)?"Remove tag '%0'%1":"Add tag '%0'%1").format([tag,_bf]);var _c5="%0; Shift-Click: %1".format([_c4,_c3]);var btn=createTiddlyButton(_c0,tag,_c5,_ae,_78(tag)?"currentTag":null);btn.setAttribute("tag",tag);}};var _c7=function(){if(_71){window.scrollTo(0,ensureVisible(_71));}if(_77()){window.scrollTo(0,ensureVisible(_77()));}};var _c8=function(e){if(!e){var e=window.event;}if(!_71){return;}var _cb=resolveTarget(e);if(_cb==_77()){return;}if(abego.isDescendantOrSelf(_71,_cb)){return;}abego.IntelliTagger.close();};addEvent(document,"click",_c8);var _cc=Story.prototype.gatherSaveFields;Story.prototype.gatherSaveFields=function(e,_ce){_cc.apply(this,arguments);var _cf=_ce.tags;if(_cf){_ce.tags=_cf.trim();}};var _d0=function(_d1){story.focusTiddler(_d1,"tags");var _d2=abego.getTiddlerField(story,_d1,"tags");if(_d2){var len=_d2.value.length;abego.setRange(_d2,len,len);window.scrollTo(0,ensureVisible(_d2));}};var _d4=config.macros.edit.handler;config.macros.edit.handler=function(_d5,_d6,_d7,_d8,_d9,_da){_d4.apply(this,arguments);var _db=_d7[0];if((_da instanceof Tiddler)&&_db=="tags"){var _dc=_d5.lastChild;_dc.onfocus=function(e){abego.IntelliTagger.assistTagging(_dc,_da);setTimeout(function(){_d0(_da.title);},100);};_dc.onkeyup=function(e){if(!e){var e=window.event;}if(e.altKey&&!e.ctrlKey&&!e.metaKey&&(e.keyCode>=48&&e.keyCode<=57)){_8b(e.keyCode==48?9:e.keyCode-49,_dc,_da);}else{if(e.ctrlKey&&e.keyCode==32){_8b(0,_dc,_da);}}if(!e.ctrlKey&&(e.keyCode==13||e.keyCode==10)){_8b(0,_dc,_da);}setTimeout(function(){abego.IntelliTagger.assistTagging(_dc,_da);},100);return false;};_81(_dc);}};var _e0=function(e){if(!e){var e=window.event;}var _e3=resolveTarget(e);var _e4=_e3.getAttribute("tiddler");if(_e4){story.displayTiddler(_e3,_e4,"IntelliTaggerEditTagsTemplate",false);_d0(_e4);}return false;};var _e5=config.macros.tags.handler;config.macros.tags.handler=function(_e6,_e7,_e8,_e9,_ea,_eb){_e5.apply(this,arguments);abego.IntelliTagger.createEditTagsButton(_eb,createTiddlyElement(_e6.lastChild,"li"));};var _ec=function(){if(_71&&_72&&!abego.isDescendantOrSelf(document,_72)){abego.IntelliTagger.close();}};setInterval(_ec,100);abego.IntelliTagger.displayTagSuggestions=function(_ed,_ee,_ef,_f0,_f1){_74=_ed;_75=abego.toSet(_ee);_76=_ef;_72=_f0;_73=_f1;if(!_71){_71=createTiddlyElement(document.body,"div",null,"intelliTaggerSuggestions");_71.style.position="absolute";}_ac();abego.openAsPopup(_71);if(_77()){var w=_77().offsetWidth;if(_71.offsetWidth<w){_71.style.width=(w-2*(_6e+_6f))+"px";}abego.moveBelowAndClip(_71,_77());}else{abego.centerOnWindow(_71);}_c7();};abego.IntelliTagger.assistTagging=function(_f3,_f4){var _f5=_90(_f3);var s=_f3.value;if(_7d(_f3)){s=_7a(s);}var _f7=s.readBracketedList();var _f8=_f7.length>0?abego.filterStrings(abego.internal.getTagManager().getPartnerRankedTags(_f7),_f5,_70):_a0(_f5,_70);abego.IntelliTagger.displayTagSuggestions(_a9(_f5,_f7),_f7,_f8,_f3,function(tag,e){if(e.shiftKey){onClickTag.call(this,e);}else{_85(tag,_f3,_f4);}});};abego.IntelliTagger.close=function(){abego.closePopup(_71);_71=null;return false;};abego.IntelliTagger.createEditTagsButton=function(_fb,_fc,_fd,_fe,_ff,id,_101){if(!_fd){_fd="[edit]";}if(!_fe){_fe="Edit the tags";}if(!_ff){_ff="editTags";}var _102=createTiddlyButton(_fc,_fd,_fe,_e0,_ff,id,_101);_102.setAttribute("tiddler",(_fb instanceof Tiddler)?_fb.title:String(_fb));return _102;};abego.IntelliTagger.getSuggestionTagsMaxCount=function(){return 100;};config.macros.intelliTagger={label:"intelliTagger",handler:function(_103,_104,_105,_106,_107,_108){var _109=_107.parseParams("list",null,true);var _10a=_109[0]["action"];for(var i=0;_10a&&i<_10a.length;i++){var _10c=_10a[i];var _10d=config.macros.intelliTagger.subhandlers[_10c];if(!_10d){abego.alertAndThrow("Unsupported action '%0'".format([_10c]));}_10d(_103,_104,_105,_106,_107,_108);}},subhandlers:{showTags:function(_10e,_10f,_110,_111,_112,_113){_b4(_10e,_74,_76?_76.length:0,_76,abego.IntelliTagger.getSuggestionTagsMaxCount());},showFavorites:function(_114,_115,_116,_117,_118,_119){_b4(_114,_76,0);},closeButton:function(_11a,_11b,_11c,_11d,_11e,_11f){var _120=createTiddlyButton(_11a,"close","Close the suggestions",abego.IntelliTagger.close);},version:function(_121){var t="IntelliTagger %0.%1.%2".format([version.extensions.IntelliTaggerPlugin.major,version.extensions.IntelliTaggerPlugin.minor,version.extensions.IntelliTaggerPlugin.revision]);var e=createTiddlyElement(_121,"a");e.setAttribute("href","http://tiddlywiki.abego-software.de/#IntelliTaggerPlugin");e.innerHTML="<font color=\"black\" face=\"Arial, Helvetica, sans-serif\">"+t+"<font>";},copyright:function(_124){var e=createTiddlyElement(_124,"a");e.setAttribute("href","http://tiddlywiki.abego-software.de");e.innerHTML="<font color=\"black\" face=\"Arial, Helvetica, sans-serif\">&copy; 2006-2007 <b><font color=\"red\">abego</font></b> Software<font>";}}};})();config.shadowTiddlers["IntelliTaggerStyleSheet"]="/***\n"+"!~IntelliTagger Stylesheet\n"+"***/\n"+"/*{{{*/\n"+".intelliTaggerSuggestions {\n"+"\tposition: absolute;\n"+"\twidth: 600px;\n"+"\n"+"\tpadding: 2px;\n"+"\tlist-style: none;\n"+"\tmargin: 0;\n"+"\n"+"\tbackground: #eeeeee;\n"+"\tborder: 1px solid DarkGray;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .currentTag   {\n"+"\tfont-weight: bold;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .suggestionNumber {\n"+"\tcolor: #808080;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .numberedSuggestion{\n"+"\twhite-space: nowrap;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .intelliTaggerFooter {\n"+"\tmargin-top: 4px;\n"+"\tborder-top-width: thin;\n"+"\tborder-top-style: solid;\n"+"\tborder-top-color: #999999;\n"+"}\n"+".intelliTaggerSuggestions .favorites {\n"+"\tborder-bottom-width: thin;\n"+"\tborder-bottom-style: solid;\n"+"\tborder-bottom-color: #999999;\n"+"\tpadding-bottom: 2px;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .normalTags {\n"+"\tpadding-top: 2px;\n"+"}\n"+"\n"+".intelliTaggerSuggestions .intelliTaggerFooter .button {\n"+"\tfont-size: 10px;\n"+"\n"+"\tpadding-left: 0.3em;\n"+"\tpadding-right: 0.3em;\n"+"}\n"+"\n"+"/*}}}*/\n";config.shadowTiddlers["IntelliTaggerMainTemplate"]="<!--\n"+"{{{\n"+"-->\n"+"<div class=\"favorites\" macro=\"intelliTagger action: showFavorites\"></div>\n"+"<div class=\"normalTags\" macro=\"intelliTagger action: showTags\"></div>\n"+"<!-- The Footer (with the Navigation) ============================================ -->\n"+"<table class=\"intelliTaggerFooter\" border=\"0\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\"><tbody>\n"+"  <tr>\n"+"\t<td align=\"left\">\n"+"\t\t<span macro=\"intelliTagger action: closeButton\"></span>\n"+"\t</td>\n"+"\t<td align=\"right\">\n"+"\t\t<span macro=\"intelliTagger action: version\"></span>, <span macro=\"intelliTagger action: copyright \"></span>\n"+"\t</td>\n"+"  </tr>\n"+"</tbody></table>\n"+"<!--\n"+"}}}\n"+"-->\n";config.shadowTiddlers["IntelliTaggerEditTagsTemplate"]="<!--\n"+"{{{\n"+"-->\n"+"<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler'></div>\n"+"<div class='title' macro='view title'></div>\n"+"<div class='tagged' macro='tags'></div>\n"+"<div class='viewer' macro='view text wikified'></div>\n"+"<div class='toolbar' macro='toolbar +saveTiddler -cancelTiddler'></div>\n"+"<div class='editor' macro='edit tags'></div><div class='editorFooter'><span macro='message views.editor.tagPrompt'></span><span macro='tagChooser'></span></div>\n"+"<!--\n"+"}}}\n"+"-->\n";config.shadowTiddlers["BSD open source license (abego Software)"]="See [[Licence|http://tiddlywiki.abego-software.de/#%5B%5BBSD%20open%20source%20license%5D%5D]].";config.shadowTiddlers["IntelliTaggerPlugin Documentation"]="[[Documentation on abego Software website|http://tiddlywiki.abego-software.de/doc/IntelliTagger.pdf]].";config.shadowTiddlers["IntelliTaggerPlugin SourceCode"]="[[Plugin source code on abego Software website|http://tiddlywiki.abego-software.de/archive/IntelliTaggerPlugin/Plugin-IntelliTagger-src.1.0.2.js]]\n";(function(){var _126=restart;restart=function(){setStylesheet(store.getTiddlerText("IntelliTaggerStyleSheet"),"IntelliTaggerStyleSheet");_126.apply(this,arguments);};})();}
// %/
''Welcome to the K2 ~AppServer Developer's Guide''

The K2 can be controlled via four different ~APIs. Three are protocol ~APIs (AMP, BVW, and VDCP) and one is the K2's native ~AppServer API. If you are new to developing K2 software you should follow these steps:

!!!1. Getting started:
[[Click here to go the Developer Guides section.|Developer Guides]] There you will find links to 2 PDF documents: one for protocols (AMP, VDCP, BVW, FTP) and one for ~AppServer API. Print these out and study them. They are our best resources for getting started with our ~APIs.

!!!2. Training Videos
[[Click here to go the Videos section.|Videos]] View the K2 Logging training video. This explains our logs, the tools we use, and how to read the logs. There are additional training videos there about the ~AppServer API and C# scripts.

!!!3. Gathering Logs
[[Click here to go the Troubleshooting section.|Troubleshooting and gathering useful logs]] This explains how to best troubleshoot your problems and what information we like to receive to best help you quickly.

!!!4. (Optional) Tracing ~AppServer API Calls
[[Click here to go the Function Trace Logging section.|Enable function trace logging]] Sometimes when developing with the ~AppServer API there are questions as to which calls to use or what parameters a call can take. Sometimes it is useful to just turn on call trace logging and then use ~AppCenter to perform a similar function. You can then learn the calls or parameters by looking at the call trace log and see the call sequence that ~AppCenter made. Tracing is also a good way to later profile your own application to make sure your application is making the calls you expect with the timing you expect (especially in threaded apps!).


!!!Other documentation:
After reading thru one of the documents above, for more specific information you should:
* visit the [[K2 API]] link on the left. 
* visit the [[Tutorial]], [[Video|Videos]], and [[Examples]] links on the left
* use the "search" box at the top
* browse this website
* [[email us any questions|Questions?]] you have

!!!Using this site:
This website uses a [[wiki|About]] to provide hyperlinked, searchable information:
* View a topic: click on a link 
* Close a topic: move your mouse over the topic's text. Buttons will appear in the upper right corner above the topic text. Select a button to close or jump to other topics. 
* Search for a topic: enter it in the search box at the top. Press the "search" button and get a list of matching topics. Click on a topic.
* Browse for a topic: look thru all entries listed on the right. Click on one.
''14 August 2007 - K2 3.2 Release available''
Version 3.2 of K2 software is now available for download here:

[[Download K2 3.2 software|http://www.grassvalley.com/downloads/servers/k2_3.2.21.746.html]]

The 3.2 software comes with a 30-day trial license. A Developer's License is available for registered developers. Contact us at ''[[GVDeveloperSupport@thomson.net|mailto:GVDeveloperSupport@thomson.net?subject=K2 3.2 developer's license request]]'' for more information.


''K2 3.2 functionality includes :''
(see section 5 of [[K2 Protocol Developer's Guide|http://www.gvgdevelopers.com/K2ProtocolDevGuide/K2_Protocol_Developers_Guide.pdf]] for additional AMP enhancements)

''Bi-directional channels:''
A channel can now act as both a player and a recorder. To switch between functionality, you simply press the play or record transport button, respectively. Programmatically, this means an application can send player and recorder commands to the same channel. No longer is the channel restricted to play-only or record-only functionality. 

Additionally, each channel can always be controlled by AMP, VDCP, or BVW protocol. As soon as the K2 is up-and-running it is ready to receive configured protocol commands. No longer does a specific protocol player or protocol recorder application need to be loaded for the channel.

''Channel ganging:''
Channel ganging allows you to link two or more channels in a "gang" to synchronize control of the channels. Ganging record channels together allows you to record multi-track clips of up to 4 video and 32 audio tracks. 

Examples:
* record a clip that has many language tracks.
* record a clip that has two key and fill video tracks

Ganging play channels together allows you to play one clip on all channels or control playing different clips on all the channels in the gang.

Examples:
* play one video on two channels: output HD on one channel, down-convert to SD on a second channel
* play a clip with one set of languages on one channel one and another set of languages on another channel
* play a key video track on one channel synchronized with a fill video track on another channel

''Audio track mapping:''
Audio track mapping lets you label video and audio tracks. The track labels can then be used to automatically route the output audio channels. Tracks can additionally be imported from other clips or WAV files to make clips with up to 4 video and 32 audio tracks.

''~AppCenter date localization:''
~AppCenter's ~MediaMonitor can now display clip date information using regional date formats.
[[Click here to go to the online K2 API.|API/index.html]] You may also [[download a compiled HTML help manual version here|API/AppServerAPI.zip]].

These lists all of the namespaces, classes, and methods exposed through the K2 API.
* K2 clip names can have a maximum of 32 characters
* K2 clip names cannot contain any of these characters: * \ / | < > : “ ?
Updated: December 17, 2008

The K2 Emulator is available here.

It is recommended that you install the K2 Emulator on a dedicated machine or a virtual machine such as [[Microsoft's Virtual PC|http://www.microsoft.com/windows/products/winfamily/virtualpc/default.mspx]]. The reason for this is to not conflict other Grass Valley software that you may have installed or other components that you may have installed for other development efforts (e.g. another Microsoft database that conflicts with the MSDE database we install for the emulator).

''Download:''
[[Click here to download the K2 Emulator Prerequisites Installer|zip/Emulator PreRequisites.zip]] - Size: 152 MB
[[Click here to download the K2 Emulator|zip/Emulator_v3.2.74.1000.zip]] - Size: 28 MB - version 3.2.74.1000 updated Dec 17, 2008

[[Test Clips]] are also available for downloading and importing into the K2 Emulator. [[Click here to download them.|Test Clips]]

''Requirements:''
The PC requirements for the emulator are the same as the [[ControlPoint PC System Requirements. Click here to see them|ControlPoint PC System Requirements]]. 

''Fixes:''
* ver 3.2.58.848 - fixed UI flicker that kept switching between Play and Record panels.
* ver 3.2.58.841 - fixed HD config files. Also changed config files so working bins are set to C:\default instead of V:\default.
* ver 3.2.58.836 - you can now record "fake" clips. The stop button also now works on playback.
* ver 3.2.58.836 - successfully tested ~RS422 protocol control using a ~RocketPort Plus 2P SMPTE PCI card
* ver 3.2.58.829 - you can now enumerate assets via AMP. The emulator uses the C: drive instead of the V: drive. There were some hard-coded "V:" path issues in the AMP listener code that have been resolved.

''Functionality:''
The K2 Emulator is the full K2 software with the hardware codec portion emulated. That means:
* You can load a clip in ~AppCenter and press play. The application will show the clip playing and the time code will progress, but since there is not a real decoder, you cannot see the output displayed anywhere.
* You can record a "fake: clip. You will record with growing length. It will not have a thumbnail since there is no real video to decode.
* Transfers and FTP are supported.
* All ~APIs are supported - AMP, BVW, VDCP, and ~AppServer over both Ethernet and ~RS422 connections.
* ~RS422 protocol control has been successfully tested using a ~RocketPort Plus 2P SMPTE RS422 PCI card. However, a USB Digi Edgeport/4 ~RS422 converter box was not recognized by the K2 emulator.

''Installation Steps:''
1. First, install the K2 Emulator Prerequisites (download from link above). Unzip the prerequisites zip file and run "setup.exe". This installs:
* Microsoft MSDE 2000 Desktop Engine
* Direct Show 9
* MSXML 4
* .NET 1.1, 2.0, and 3.0
* Visual C++ 2005 ~SP1 redistributable
* ~QuickTime 7.0.4
* Install Shield MSI

2. Next, install the K2 Emulator (download from link above). Unzip the emulator zip file and run "setup.exe".
After the emulator is installed it will ask to reboot your system. Say Yes.

''Configuration:''
The K2 Emulator can be configured as:
* an SD system with channels C1, C2, C3, C4
* an HD system with channels R1, P1, R2, P2
The default configuration is an SD system.

To change the configuration to an HD system in the c:\profile\config directory:
* copy hd-config.xml to config.xml
* copy hd-systemInfo.xml to systemInfo.xml

To change the configuration to an SD system in the c:\profile\config directory:
* copy sd-config.xml to config.xml
* copy sd-systemInfo.xml to systemInfo.xml

Make sure that the config.xml and systemInfo.xml are not set to read-only. The emulator needs read/write access to these files.

''Starting:''
1. Click Start / Run and type "log". This starts the K2 log viewer so that you can see the services starting up.
2. On the desktop, double-click "~WinRtpMgr". This starts the K2 services.
3. Watch the the log viewer for startup messages. Be patient.
4. If you do not have an ~RS422 serial card connected you will likely see "invalid serial port" messages. This is normal and expected.
5. Look for messages that all controllers (channels) are created. At this point the services are up and running.
6. Start ~AppCenter either from the ~WinRtpMgr icon or the desktop.
7. Login using an administrator username and password that you have setup for your system.

~AppCenter has a default 30-day trial license. Contact ''[[GVDeveloperSupport@thomson.net|mailto:GVDeveloperSupport@thomson.net?subject=Emulator license]]'' to extend it.

From here on you can use the ~WinRtpMgr GUI to start ~AppCenter and start/stop the services.

''Limitations:''
* Imported MXF files do not show thumbnails of the clips correctly. Imported GXF files do not have this problems.
* ~FTPing to the emulator results in files in the V: drive instead of the C: drive. These will not be accessible from the Emulator. The best way to import or export files to the emulator is to use ~AppCenter's "Send To..." and "Import..." menu options.
* Striping clips does not work in the emulator. It depends on a lower level driver that is not present in the emulator.
* In ~AppCenter in a channel's Option / Timecode panel, if you choose any of the “Recorded Timecode” choices, the value that will be used is just a simulated timecode that is ever-increasing.  The reason for this is that on a real K2, timecode information is provided from hardware (and that hardware interface is only simulated by the emulator).  However, if you choose the “Generated Timecode” option in the emulator, when playing back a clip you will get a timecode that is relative to a clip starting point of 00:00:00,00.  On a real K2 of course, all timecode modes are supported, and if you choose “system clock” as the time of day source, and select the “Time of Day” timecode mode, you would be able to record clips using the system clock as their timecode source.”
* Scheduled context changes are not implemented in the K2 Emulator. On a real K2 Client these are messages we pass to the real-time system telling it to do something at a specified timecode (e.g. start playing at timecode "xx:xx:xx.xx"). Pre-roll commands in protocols are also implemented using scheduled context changes. So for testing purposes with the emulator, you can still watch the log output to see that a "Play" command is received.

''Troubleshooting:''
1. __Installation trouble or conflicts on start-up__:
We seen conflicts when other software is already installed. This could be other Microsoft databases (MSSQL, MSDE, etc) or other Grass Valley software (Profile software in c:\profile). One way that you can guarantee that you are dealing with a clean environment without having to make changes to your system is to install the emulator on a virtual PC. This will give you an isolated environment to run in without requiring you to change your software.

2. __Cannot login to ~AppCenter or cannot see and control channels in the emulator__:
When ~AppCenter starts it checks to see if a user has administrator rights before granting access to channels. It is checking for administrator rights by looking for one of these conditions:

a. user is part of the group "BUILTIN\\Administrators" (this works for US versions of Windows XP), or
b. user is part of the group "<machinename>\~K2Administrators". 

If you are using a non-US version of Windows XP, there are two ways of resolving this problem:

''First option:''
- Change all of your Windows "Regional and language options" to "US / English"
- add your username to the "BUILTIN\Administrators" group
- reboot
- login to ~AppCenter with username.

''Second option:''
- create a local group name on the emulator machine called "~K2Administrators". 
- create a new local user on the same machine and make them a member of the "~K2Administrators" group. 
- restart ~AppCenter and login with the new username.
Below are the ~MIBs used for the K2 Client, K2 Server, and K2 Appliances as well as their purpose. Right-click any link and select "save as" to save the MIB.
 
Note that the device may support additional ~MIBs based on third party software/hardware (for instance the Intel or Broadcom ~NICs on the Dell, or the IPX protocol ~MIBs on Windows). These are not listed below. 
 
The ~MIBs listed below are the ones that are always present on the device and portray adequate information about the device. For determining whether additional ~MIBs are supported by the operating system or IH/SV (Independent hardware/software vendors), you can perform a “MIB walk” operation on the device using conventional SNMP utilities and determine additional (non Grass Valley) supported ~MIBs.

!!K2 SDI Client:
''[[Click here to download all K2 Client MIBs|MIBs/K2ClientMIBs.zip]]''

[[RFC1213-MIB.mib|MIBs/RFC1213-MIB.mib]] (~RFC1213-MIB)
~MIB-2 support as implemented by Microsoft for the Windows operating system.
 
[[hostmib.mib|MIBs/hostmib.mib]] (~HOST-RESOURCES-MIB)
Generic system information as implemented by Microsoft for the Windows operating system
 
[[Lmmib2.mib|MIBs/Lmmib2.mib]] (~LanMgr-Mib-II-MIB)
Generic Windows networking, user account and service information as implemented by Microsoft for the Windows operating system
 
[[SUPERMICRO-SMI.my|MIBs/SUPERMICRO-SMI.my]] (~SUPERMICRO-SMI)
[[SUPERMICRO-HEALTH-MIB.my|MIBs/SUPERMICRO-HEALTH-MIB.my]] (~SUPERMICRO-HEALTH-MIB)
K2 client motherboard electromechanical sensor information (motherboard temperature hotspots, CPU fan, voltages and so on)
 
[[MEGARAID.MIB|MIBs/MEGARAID.MIB]] (~RAID-Adapter-MIB)
K2 Client internal ~RAID-1 SCSI drive and controller information
 
''Grass Valley ~MIBs:'' 
[[gvg-reg.mi2|MIBs/gvg-reg.mi2]] (~GVG-REG)
Grass Valley SMI enterprise namespace
 
[[gvg-element.mi2|MIBs/gvg-element.mi2]] (~GVG-ELEMENT-MIB)
Common object definitions for a Grass Valley device
- Generic device tracking information
- SNMP trap target configuration
- Generic IO/signal status information
 
[[gvg-prod.mi2|MIBs/gvg-prod.mi2]] (~GVG-PROD-REG)
Product sysObjectOID registrations for the Grass Valley devices
 
[[gvg-drs.mi2|MIBs/gvg-drs.mi2]] (~GVG-DRS-MIB)
Video disk recorder/server status information
 
[[gvg-tcm.mi2|MIBs/gvg-tcm.mi2]] (~GVG-TCM-MIB)
Media transfer (import/export) statistical information
 
[[gvg-manclient.mi2|MIBs/gvg-manclient.mi2]] (~GVG-MANCLIENT-MIB)
SAN client status information
[Available only when the K2 client is connected to a SAN (~FibreChannel or iSCSI]

!!K2 Server (Dell ~PowerEdge based):  
''[[Click here to download all K2 Server MIBs|MIBs/K2ServerMIBs.zip]]''

[[RFC1213-MIB.mib|MIBs/RFC1213-MIB.mib]] (~RFC1213-MIB)
~MIB-2 support as implemented by Microsoft for the Windows operating system.
 
[[hostmib.mib|MIBs/hostmib.mib]] (~HOST-RESOURCES-MIB)
Generic system information as implemented by Microsoft for the Windows operating system.
 
[[Lmmib2.mib|MIBs/Lmmib2.mib]] (~LanMgr-Mib-II-MIB)
Generic Windows networking, user account and service information as implemented by Microsoft for the Windows operating system.
 
[[mssql.mib|MIBs/mssql.mib]] (~MSSQLSERVER-MIB)
Microsoft SQL Server information
 
[[10892.mib|MIBs/10892.mib]] (~MIB-Dell-10892)
Dell ~PowerEdge chassis related electro-mechanical status information
 
[[arymgr.mib|MIBs/arymgr.mib]] (~ArrayManager-MIB)
Dell ~RAID1 system disk (PERC) and controller information
 
''Grass Valley ~MIBs:''  
[[gvg-reg.mi2|MIBs/gvg-reg.mi2]] (~GVG-REG)
Grass Valley SMI enterprise namespace
 
[[gvg-element.mi2|MIBs/gvg-element.mi2]] (~GVG-ELEMENT-MIB)
Common object definitions for a Grass Valley managed entity
- Generic device tracking information
- SNMP trap target configuration
 
[[gvg-prod.mi2|MIBs/gvg-prod.mi2]] (~GVG-PROD-REG)
Product sysObjectOID registrations for the Grass Valley devices
        
[[gvg-ssr.mi2|MIBs/gvg-ssr.mi2]] (~GVG-SSR-MIB)
K2 Storage roles configured for the server by K2 Configuration and their status information 
 
[[gvg-sbs.mi2|MIBs/gvg-sbs.mi2]] (~GVG-SBS-MIB)
K2 iSCSI Bridge and TOE (TCP Offload Engine) related status information
[Available ONLY if the server is configured to be an iSCSI Bridge]
 
[[gvg-manfsm.mi2|MIBs/gvg-manfsm.mi2]] (~GVG-MANFSM-MIB)
Video File System and Clip Database (FSM) related status information
[Available only if the server is configured to be either video file-system server, and/or movie database server]
 
[[gvg-tcm.mi2|MIBs/gvg-tcm.mi2]] (~GVG-TCM-MIB) 
Media transfer (import/export) statistical information
[Available only if the server is configured to be a transfer/FTP/hotbins server]
 
[[gvg-manclient.mi2|MIBs/gvg-manclient.mi2]] (~GVG-MANCLIENT-MIB)
SAN client status information
[Available only when the server is a video file system and/or movie database client to another K2 FSM server - for instance only an FTP server or a NAS server]
 
!!K2 Appliance (Generic Windows computer based):
''[[Click here to download all K2 Appliance MIBs|MIBs/K2ApplianceMIBs.zip]]''

''For details on the hardware/chassis running the K2 Appliance, check the chassis vendor’s ~MIBs.''
  
[[RFC1213-MIB.mib|MIBs/RFC1213-MIB.mib]] (~RFC1213-MIB)
~MIB-2 support as implemented by Microsoft for the Windows operating system.
 
[[hostmib.mib|MIBs/hostmib.mib]] (~HOST-RESOURCES-MIB)
Generic system information as implemented by Microsoft for the Windows operating system.
 
[[Lmmib2.mib|MIBs/Lmmib2.mib]] (~LanMgr-Mib-II-MIB)
Generic Windows networking, user account and service information as implemented by Microsoft for the Windows operating system.
 
''Grass Valley ~MIBs:''
[[gvg-reg.mi2|MIBs/gvg-reg.mi2]] (~GVG-REG)
Grass Valley SMI enterprise namespace
 
[[gvg-element.mi2|MIBs/gvg-element.mi2]] (~GVG-ELEMENT-MIB)
Common object definitions for a Grass Valley managed entity
- Generic device tracking information
- SNMP trap target configuration
 
[[gvg-prod.mi2|MIBs/gvg-prod.mi2]] (~GVG-PROD-REG)
Product sysObjectOID registrations for the Grass Valley devices
        
[[gvg-ssr.mi2|MIBs/gvg-ssr.mi2]] (~GVG-SSR-MIB)
K2 Storage roles configured for the appliance by K2 Configuration and their status information 
 
[[gvg-tcm.mi2|MIBs/gvg-tcm.mi2]] (~GVG-TCM-MIB) 
Media transfer (import/export) statistical information
[Available only if the server is configured to be a transfer/FTP/hotbins server]
 
[[gvg-manclient.mi2|MIBs/gvg-manclient.mi2]] (~GVG-MANCLIENT-MIB)
SAN client status information
[Available only when the server is a video file system and/or movie database client to another K2 FSM server - for instance only an FTP server or a NAS server]
!1. Summit Functionality:
The K2 Summit Client is a superset of current K2 HD client functionality. 

''New features:''
* 2 or 4  bi-directional SD & HD channels
** supported compression formats: DVCPRO HD (~DV100), ~DVCPRO25/50, DVCAM, DV
* Live Mode - quick record-to-play in less than 0.5 seconds (requires ~AppCenterPro)
* Live video display for each channel in ~AppCenter (current K2 displays just a key frame, local ~AppCenter only)
* Multiviewer VGA output (requires ~AppCenterPro)
** Quad split VGA output shows all four channels live, each with timecode, audio level meters, and transport control indicators
* Built-in video/audio mix effects on each channel (requires ~AppCenterPro)
** Fade to matte color or dissolves between clips
* 2 RU, ruggedized, easy to service chassis for OB vans with dual power supplies
* Embedded Operating System on Flash Memory
* 300 GB or 450 GB 15K rpm SAS internal drive options
* ~QuickTime compatible file system
* Product Frame compatibility for easy installs and upgrades
''Same features as K2:''
* High quality slo-mo with line interpolation
* Smooth jog/shuttle performance using K2 “Predictive Fetching” technology
* 16 audio channels (32 on disk) per channel  
** 16 tracks Embedded AES/EBU – ~PCM16, ~PCM24, Dolby E, ~AC-3
** 4 additional AES/EBU tracks for a total of 8 AES/EBU tracks per channel on an D-sub connector (current K2 is 4 AES/EBU tracks per video channel)
* Audio & CC/Teletext import capability for multilingual support
* Scrub audio and audio click elimination
* audio mapping
* SNMP monitoring with ~NetCentral
* SD/HD up and down conversion with high quality filters
* 1080i/720p cross conversion
* Clip-by-clip aspect ratio conversion
* New ~AppCenter dark look-and-feel
!2. Summit API Documentation
If you are new to developing K2 or Summit software using the ~AppServer API, please visit the [[Introduction section|Introduction]] section. The Summit API is a super-set of the K2 API. New Summit API features include:

''Documented sections:''
* ''[[Live Play]]'' - start playing back a recording clip within a 0.5 second of the record position (requires ~AppCenter Pro)
* ''[[Flying Mix Effects]]'' - create flying mix effects between the foreground and background contexts of a channel (requires ~AppCenter Pro).
* ''[[Playlist Mix Effects]]'' - create a playlist with section and event mix effects transitions (requires ~AppCenter Pro).

''To be documented soon:''
* ''Sub-bins'' - bins can now have sub-bins. K2 only had a bin depth of one
!3. Summit Emulator
The [[Summit Emulator]] is available for download. Information about it can be found here: [[Summit Emulator]]
Universal Resource Identifiers (~URIs) on the K2 are defined as follows:
{{{
 edl/cmf//<machine>/<volume>/<bin>/<name>
}}}
"edl" stands for "edit decision list"
"cmf" stands for "common movie format"
<machine> is the K2's host name or IP address (e.g. "K2-123")
<volume> is the volume or drive letter (e.g. "V:")
<bin> is the bin or directory name (e.g. "default")
<name> is the asset or clip's name (e.g. "Clip")

Note: the "edl" and "cmf" designators are legacy designators from the Profile days.

An example URI:
{{{
 edl/cmf//K2-123/V:/default/Clip
}}}
On a Summit Client you now have the ability to play a currently recording asset in Live Mode. This means that the player will play within a 0.5 second of the record position.

''@@color(red):Note:@@'' the Summit must have an ~AppCenter Pro license for this feature to work.

''@@color(red):Note:@@'' playing live only works if the asset is currently being recorded on a channel that is on the same Summit Client as the player channel.

''Check if a channel is in Live Mode:''
You can check if a channel is in Live Mode by querying a ~ChanStatus object's "~CountdownStr" property (see [[Get channel status properties]]). Normally, this property would return a timecode string representing the amount of play time left on the timeline. In Live Mode this property will return the string "Live".
{{{
string countdown = (string) ichanStatus.GetStatus("CountdownStr");
}}}

''Setup a channel for Live Mode:''
Full sample code can be downloaded from the [[CS-Script Sample Scripts]] section. Look for the sample named [[LivePlay.cs|CS-Script/LivePlay.cs]].

Below is a code snippet showing how to setup Live Mode on a player.
{{{
[C#]
// setup defaults
string volume = "V:";
string bin = "default";
string clip = "Clip";
string clipUri = String.Format("edl/cmf//local/{0}/{1}/{2}", volume, bin, clip);

// after starting a recording, you need to wait until the clip is 
// under construction. if you don't wait and you tell the player to 
// go into live mode, the player will cue to the start of the clip 
// instead and you will see this message in the log:
// "Can't go live: not under construction. Cueing to 0."

// wait until the clip is under construction...
while ( true != (bool) mediaMgr.GetProperty(clipUri, "underConstruction") )
{
	Thread.Sleep(50);
}						

// load the recording clip onto the player
player.Load(clipUri);

// Cue to the live position and start playing. in this case the command
// name "cue" is a bit of a misnomer since this command does more than
// just cueing here. it cues to the latest updated play position which
// will be within a 0.5 second of the record position and starts playing.
// 
// note: playing live only works if the asset is currently being recorded
// on a channel that is on the same Summit as the player channel.
player.Cue("live");
}}}
Below is sample code showing how to write debug, status, warning, error, and fatal messages to the K2's log.

{{{
[C#]
// create a log
IAppServerLog log = iappServer.CreateLog(appName);

// write different message types
log.LogMsg(EntryPtMsgType.eDebugMsg, 0, "debug message");
log.LogMsg(EntryPtMsgType.eStatusMsg, 0, "status message");
log.LogMsg(EntryPtMsgType.eWarningMsg, 0, "warning message");
log.LogMsg(EntryPtMsgType.eErrorMsg, 0, "error message");
log.LogMsg(EntryPtMsgType.eFatalMsg, 0, "fatal message");
}}}
{{{
[C++]
// create a log
IAppServerLogPtr spLog;
hr = spAppServer->CreateLog(m_bstrAppName, &spLog);

// write different message types
hr = spLog->LogMsg(EntryPtMsgType_eDebugMsg, 0, _bstr_t("debug message"));
hr = spLog->LogMsg(EntryPtMsgType_eStatusMsg, 0, _bstr_t("status message"));
hr = spLog->LogMsg(EntryPtMsgType_eWarningMsg, 0, _bstr_t("warning message"));
hr = spLog->LogMsg(EntryPtMsgType_eErrorMsg, 0, _bstr_t("error message"));
hr = spLog->LogMsg(EntryPtMsgType_eFatalMsg, 0, _bstr_t("fatal message"));
}}}
[[Introduction]]
[[What's New]]
[[Change Notices]]
[[Tutorial]]
[[Videos]]
[[Examples]]
[[K2 API]]
[[Tips]]
[[History]]
[[About]]
[[Questions?]]
[img[rss|images/rss.jpg]] [[RSS|RssFeed]]
----
^^© 2009 [[Grass Valley|http://www.grassvalley.com]]^^
<!--{{{-->
<link rel='alternate' type='application/rss+xml' title='RSS' href='index.xml'/>
<META name="description" content="The Grass Valley K2 AppServer Developer's Guide demonstrates how to develop custom software solutions for controlling and getting information from the Grass Valley K2 server.">
<META name="keywords" content="Grass,Valley,K2,Developer,Guide,AppServer,API,Thomson,software">
<!--}}}-->
''Available in K2 3.2.74 software:''
The code below shows how you can take an external data file (*.N0) and:

1) merge all of it to a clip
2) merge part of it to a clip
3) replace a clip's ancillary data track with it, or
4) add it to a clip that does not have an ancillary track

Copy the code below to your application, then uncomment the example section you want to try each different type of merge or replace operation. Also, see the [[CS-Script Sample Scripts]] section for a full sample script that you can download and test.
{{{
C#
// create a media mgr
using ( IMediaMgr mediaMgr = connection.AppServer.CreateMediaMgr(appName) )
{
	// define clip to modify
	string volume = "V:";
	string bin = "default";
	string clipname = "Clip";
	string dataFile = "C:\\temp\\anc_data.N0";
	
	string clipURI = String.Format("edl/cmf//{0}/{1}/{2}/{3}", host, volume, bin, clipname);

	// get an editor for clip
	using ( ISimpleEditor editor = (ISimpleEditor) mediaMgr.CreateEditor(clipURI) )
	{
		try
		{
			// cast it to a track editor
			ITrackEditor2 trackEditor = (ITrackEditor2) editor;
		
			/////////////////////////////////////////////////////
			// Example 1: Merge the ENTIRE datafile into the clip
			/////////////////////////////////////////////////////
			
			// srcFieldIn = -1 == start of file
			// srcFieldOut = -1 == end of file, 
			// destFieldPos = 0 == field 0 in destination. note: you can use a timecode string too (i.e. "00:00:00.00") 
			// merge = 1 == merge, rather than replace
			string ancFileName = trackEditor.MergeAncData (dataFile, -1, -1, 0, 1);

			// commit the change
			trackEditor.CommitAncData();

/*								
			//////////////////////////////////////////////////////////////////
			// Example 2: Merge PART of the datafile (first 300 fields) into the clip
			//////////////////////////////////////////////////////////////////
			
			// srcFieldIn = 0 == field 0
			// srcFieldOut = 300 == up to field 300 
			// destFieldPos = 0 == field 0 in destination. note: you can use a timecode string too (i.e. "00:00:00.00") 
			// merge = 1 == merge, rather than replace
			string ancFileName = trackEditor.MergeAncData (dataFile, 0, 300, 0, 1);

			// commit the change
			trackEditor.CommitAncData();
*/

/*
			//////////////////////////////////////////////////////////////////
			// Example 3: Replace or Add the clip's ancillary data track
			//////////////////////////////////////////////////////////////////
			
			// srcFieldIn = -1 == start of file
			// srcFieldOut = -1 == end of file, 
			// destFieldPos = 0 == field 0 in destination. note: you can use a timecode string too (i.e. "00:00:00.00") 
			// merge = 0 == replace, rather than merge
			
			// Note: if replacing or adding (i.e. the original clip does not have an ancillary data track) rather than 
			// merging then you first need to copy the local data file over to the K2 Client's V: drive. To do this, you 
			// should copy the local file to a location and name on the K2 Client's V: drive that looks like 
			// "V:\PDR\<bin name>\\<unique guid>.N0". Then you can use that file with the TrackEditor's MergeAncData
			// call to replace the ancillary data track.
			
			// first generate unique guid name & path
			string newDataFile = String.Format("V:\\PDR\\{0}\\{1}.N0", bin, System.Guid.NewGuid().ToString("N"));

			// if the newDataFile does not exists
			if ( File.Exists(newDataFile) )
			{
				Console.WriteLine("ERROR: Cannot copy '{0}' to '{1}'. File '{1}' already exists.", dataFile, newDataFile);
				return -1;
			}								

			// copy local data file to V: drive
			File.Copy( dataFile, newDataFile );									
			// replace the clip's anc data track
			trackEditor.MergeAncData (newDataFile, -1, -1, 0, 0);

			// commit the change
			trackEditor.CommitAncData();
			
			string ancFileName = newDataFile;
*/

			// output new ancillary file info
			Console.WriteLine("The new ancillary filename is located at '{0}'", ancFileName);
		}
		catch (Exception e)
		{
			Console.WriteLine(e);
		}
		finally
		{
			// Very Important: make sure that you detach the editor when you're done. If not, you will
			// have references to the movie hanging around. This will cause you trouble later when you 
			// try to delete or move the clip, but can't because of outstanding references to the clip.
			editor.Detach();								
		}
		
	} // when using block goes out of scope the simpleeditor is disposed
	
} // when using block goes out of scope the mediamgr is disposed
}}}
To find out more information about [[Grass Valley K2 error codes click here|Grass Valley K2 error codes]].

Error codes that are not defined by Grass Valley are most often defined by Microsoft. [[Click here to see a list of Microsoft's System Error Codes (0 - 15999)|http://msdn2.microsoft.com/en-us/library/ms681381.aspx]].

You can also get more information about these errors from a DOS prompt by typing "net helpmsg" like this:
{{{
C:\>net helpmsg 10054

An established connection was aborted by the software in your host machine.
}}}

Also you can download [[Microsoft's Error Code Lookup tool|http://www.microsoft.com/downloads/details.aspx?familyid=be596899-7bb8-4208-b7fc-09e02a13696c&displaylang=en]]. Running it and restricting the results to the "winerror.h" table often gives you the error information you want. 

For example:
{{{
C:\Err>err /winerror.h 10054
# winerror.h selected.
# for decimal 10054 / hex 0x2746 :
 WSAECONNRESET winerror.h
# An existing connection was forcibly closed by the remote
# host.
# 1 matches found for "10054"
}}}
If restricting the table results in a "NOT FOUND" message:
{{{
C:\Err>err /winerror.h 0x80131501
# winerror.h selected.
# as an HRESULT: Severity: FAILURE (1), Facility: 0x13, Code 0x1501
# NOT FOUND: 0x80131501
}}}
Try running the error code lookup tool again without restricting the results:
{{{
C:\Err>err 0x80131501
# for hex 0x80131501 / decimal -2146233087 :
 COR_E_SYSTEM corerror.h
# MessageText:
# The base class for the runtime's "less serious" exceptions
# 1 matches found for "0x80131501"
}}}
<div class='header' macro='gradient vert #000 #000'>
<div class='headerShadow'>
<span class='siteTitle'><b>abego</b>Extensions<br/></span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>
</div>

<div class='headerForeground'>
<table width="1200" cellpadding="0">
<tr><td width="125"></td><td>
<span class='siteTitle'><b>K2 AppServer Developer's Guide</b><br/></span>
</td></tr>
<tr><td width="125"></td><td>
<span class='siteSubtitle' refresh='content' tiddler='SiteSubtitle'></span>&nbsp;&nbsp;<span macro='search "" sizeTextbox:59 buttonAtRight:1'></span>
</td></tr></table>
</div>

</div>
<div id='mainMenu' refresh='content' tiddler='MainMenu'></div>
<div id='sidebar'>
<div id='sidebarOptions' refresh='content' tiddler='SideBarOptions'></div>
<div id='sidebarTabs' refresh='content' force='true' tiddler='SideBarTabs'></div>
</div>

<div id='displayArea'>
<div id='messageArea'></div>
<div id='tiddlerDisplay'></div>
</div>
/***
|<html><a name="Top"/></html>''Name:''|PartTiddlerPlugin|
|''Version:''|1.0.9 (2007-07-14)|
|''Source:''|http://tiddlywiki.abego-software.de/#PartTiddlerPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
|''CoreVersion:''|2.1.3|
|''Browser:''|Firefox 1.0.4+; InternetExplorer 6.0|
!Table of Content<html><a name="TOC"/></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Description',null, event)">Description, Syntax</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Applications',null, event)">Applications</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('LongTiddler',null, event)">Refering to Paragraphs of a Longer Tiddler</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Citation',null, event)">Citation Index</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('TableCells',null, event)">Creating "multi-line" Table Cells</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Tabs',null, event)">Creating Tabs</a></html>
** <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Sliders',null, event)">Using Sliders</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Revisions',null, event)">Revision History</a></html>
* <html><a href="javascript:;" onclick="window.scrollAnchorVisible('Code',null, event)">Code</a></html>
!Description<html><a name="Description"/></html>
With the {{{<part aPartName> ... </part>}}} feature you can structure your tiddler text into separate (named) parts. 
Each part can be referenced as a "normal" tiddler, using the "//tiddlerName//''/''//partName//" syntax (e.g. "About/Features").  E.g. you may create links to the parts (e.g. {{{[[Quotes/BAX95]]}}} or {{{[[Hobbies|AboutMe/Hobbies]]}}}), use it in {{{<<tiddler...>>}}} or {{{<<tabs...>>}}} macros etc.


''Syntax:'' 
|>|''<part'' //partName// [''hidden''] ''>'' //any tiddler content// ''</part>''|
|//partName//|The name of the part. You may reference a part tiddler with the combined tiddler name "//nameOfContainerTidder//''/''//partName//. <<br>>If you use a partName containing spaces you need to quote it (e.g. {{{"Major Overview"}}} or {{{[[Shortcut List]]}}}).|
|''hidden''|When defined the content of the part is not displayed in the container tiddler. But when the part is explicitly referenced (e.g. in a {{{<<tiddler...>>}}} macro or in a link) the part's content is displayed.|
|<html><i>any&nbsp;tiddler&nbsp;content</i></html>|<html>The content of the part.<br>A part can have any content that a "normal" tiddler may have, e.g. you may use all the formattings and macros defined.</html>|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. 'or' means that exactly one of the two alternatives must exist.~~|
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!Applications<html><a name="Applications"/></html>
!!Refering to Paragraphs of a Longer Tiddler<html><a name="LongTiddler"/></html>
Assume you have written a long description in a tiddler and now you want to refer to the content of a certain paragraph in that tiddler (e.g. some definition.) Just wrap the text with a ''part'' block, give it a nice name, create a "pretty link" (like {{{[[Discussion Groups|Introduction/DiscussionGroups]]}}}) and you are done.

Notice this complements the approach to first writing a lot of small tiddlers and combine these tiddlers to one larger tiddler in a second step (e.g. using the {{{<<tiddler...>>}}} macro). Using the ''part'' feature you can first write a "classic" (longer) text that can be read "from top to bottom" and later "reuse" parts of this text for some more "non-linear" reading.

<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!!Citation Index<html><a name="Citation"/></html>
Create a tiddler "Citations" that contains your "citations". 
Wrap every citation with a part and a proper name. 

''Example''
{{{
<part BAX98>Baxter, Ira D. et al: //Clone Detection Using Abstract Syntax Trees.// 
in //Proc. ICSM//, 1998.</part>

<part BEL02>Bellon, Stefan: //Vergleich von Techniken zur Erkennung duplizierten Quellcodes.// 
Thesis, Uni Stuttgart, 2002.</part>

<part DUC99>Ducasse, Stéfane et al: //A Language Independent Approach for Detecting Duplicated Code.// 
in //Proc. ICSM//, 1999.</part>
}}}

You may now "cite" them just by using a pretty link like {{{[[Citations/BAX98]]}}} or even more pretty, like this {{{[[BAX98|Citations/BAX98]]}}}.

<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!!Creating "multi-line" Table Cells<html><a name="TableCells"/></html>
You may have noticed that it is hard to create table cells with "multi-line" content. E.g. if you want to create a bullet list inside a table cell you cannot just write the bullet list
{{{
* Item 1
* Item 2
* Item 3
}}}
into a table cell (i.e. between the | ... | bars) because every bullet item must start in a new line but all cells of a table row must be in one line.

Using the ''part'' feature this problem can be solved. Just create a hidden part that contains the cells content and use a {{{<<tiddler >>}}} macro to include its content in the table's cell.

''Example''
{{{
|!Subject|!Items|
|subject1|<<tiddler ./Cell1>>|
|subject2|<<tiddler ./Cell2>>|

<part Cell1 hidden>
* Item 1
* Item 2
* Item 3
</part>
...
}}}

Notice that inside the {{{<<tiddler ...>>}}} macro you may refer to the "current tiddler" using the ".".

BTW: The same approach can be used to create bullet lists with items that contain more than one line.

<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!!Creating Tabs<html><a name="Tabs"/></html>
The build-in {{{<<tabs ...>>}}} macro requires that you defined an additional tiddler for every tab it displays. When you want to have "nested" tabs you need to define a tiddler for the "main tab" and one for every tab it contains. I.e. the definition of a set of tabs that is visually displayed at one place is distributed across multiple tiddlers.

With the ''part'' feature you can put the complete definition in one tiddler, making it easier to keep an overview and maintain the tab sets.

''Example''
The standard tabs at the sidebar are defined by the following eight tiddlers:
* SideBarTabs
* TabAll
* TabMore
* TabMoreMissing
* TabMoreOrphans
* TabMoreShadowed
* TabTags
* TabTimeline

Instead of these eight tiddlers one could define the following SideBarTabs tiddler that uses the ''part'' feature:
{{{
<<tabs txtMainTab 
    Timeline Timeline SideBarTabs/Timeline 
    All 'All tiddlers' SideBarTabs/All 
    Tags 'All tags' SideBarTabs/Tags 
    More 'More lists' SideBarTabs/More>>
<part Timeline hidden><<timeline>></part>
<part All hidden><<list all>></part>
<part Tags hidden><<allTags>></part>
<part More hidden><<tabs txtMoreTab 
    Missing 'Missing tiddlers' SideBarTabs/Missing 
    Orphans 'Orphaned tiddlers' SideBarTabs/Orphans 
    Shadowed 'Shadowed tiddlers' SideBarTabs/Shadowed>></part>
<part Missing hidden><<list missing>></part>
<part Orphans hidden><<list orphans>></part>
<part Shadowed hidden><<list shadowed>></part>
}}}

Notice that you can easily "overwrite" individual parts in separate tiddlers that have the full name of the part.

E.g. if you don't like the classic timeline tab but only want to see the 100 most recent tiddlers you could create a tiddler "~SideBarTabs/Timeline" with the following content:
{{{
<<forEachTiddler 
		sortBy 'tiddler.modified' descending 
		write '(index < 100) ? "* [["+tiddler.title+"]]\n":""'>>
}}}
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!!Using Sliders<html><a name="Sliders"/></html>
Very similar to the build-in {{{<<tabs ...>>}}} macro (see above) the {{{<<slider ...>>}}} macro requires that you defined an additional tiddler that holds the content "to be slid". You can avoid creating this extra tiddler by using the ''part'' feature

''Example''
In a tiddler "About" we may use the slider to show some details that are documented in the tiddler's "Details" part.
{{{
...
<<slider chkAboutDetails About/Details details "Click here to see more details">>
<part Details hidden>
To give you a better overview ...
</part>
...
}}}

Notice that putting the content of the slider into the slider's tiddler also has an extra benefit: When you decide you need to edit the content of the slider you can just doubleclick the content, the tiddler opens for editing and you can directly start editing the content (in the part section). In the "old" approach you would doubleclick the tiddler, see that the slider is using tiddler X, have to look for the tiddler X and can finally open it for editing. So using the ''part'' approach results in a much short workflow.

<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!Revision history<html><a name="Revisions"/></html>
* v1.0.9 (2007-07-14)
** Bugfix: Error when using the SideBarTabs example and switching between "More" and "Shadow". Thanks to cmari for reporting the issue.
* v1.0.8 (2007-06-16)
** Speeding up display of tiddlers containing multiple pard definitions. Thanks to Paco Rivière for reporting the issue.
** Support "./partName" syntax inside <<tabs ...>> macro
* v1.0.7 (2007-03-07)
** Bugfix: <<tiddler "./partName">> does not always render correctly after a refresh (e.g. like it happens when using the "Include" plugin). Thanks to Morris Gray for reporting the bug.
* v1.0.6 (2006-11-07)
** Bugfix: cannot edit tiddler when UploadPlugin by Bidix is installed. Thanks to José Luis González Castro for reporting the bug.
* v1.0.5 (2006-03-02)
** Bugfix: Example with multi-line table cells does not work in IE6. Thanks to Paulo Soares for reporting the bug.
* v1.0.4 (2006-02-28)
** Bugfix: Shadow tiddlers cannot be edited (in TW 2.0.6). Thanks to Torsten Vanek for reporting the bug.
* v1.0.3 (2006-02-26)
** Adapt code to newly introduced Tiddler.prototype.isReadOnly() function (in TW 2.0.6). Thanks to Paulo Soares for reporting the problem.
* v1.0.2 (2006-02-05)
** Also allow other macros than the "tiddler" macro use the "." in the part reference (to refer to "this" tiddler)
* v1.0.1 (2006-01-27)
** Added Table of Content for plugin documentation. Thanks to RichCarrillo for suggesting.
** Bugfix: newReminder plugin does not work when PartTiddler is installed. Thanks to PauloSoares for reporting.
* v1.0.0 (2006-01-25)
** initial version
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!Code<html><a name="Code"/></html>
<html><sub><a href="javascript:;" onclick="window.scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
***/
//{{{
//============================================================================
//                           PartTiddlerPlugin

// Ensure that the PartTiddler Plugin is only installed once.
//
if (!version.extensions.PartTiddlerPlugin) {



version.extensions.PartTiddlerPlugin = {
    major: 1, minor: 0, revision: 9,
    date: new Date(2007, 6, 14), 
    type: 'plugin',
    source: "http://tiddlywiki.abego-software.de/#PartTiddlerPlugin"
};

if (!window.abego) window.abego = {};
if (version.major < 2) alertAndThrow("PartTiddlerPlugin requires TiddlyWiki 2.0 or newer.");

//============================================================================
// Common Helpers

// Looks for the next newline, starting at the index-th char of text. 
//
// If there are only whitespaces between index and the newline 
// the index behind the newline is returned, 
// otherwise (or when no newline is found) index is returned.
//
var skipEmptyEndOfLine = function(text, index) {
	var re = /(\n|[^\s])/g;
	re.lastIndex = index;
	var result = re.exec(text);
	return (result && text.charAt(result.index) == '\n') 
			? result.index+1
			: index;
}


//============================================================================
// Constants

var partEndOrStartTagRE = /(<\/part>)|(<part(?:\s+)((?:[^>])+)>)/mg;
var partEndTagREString = "<\\/part>";
var partEndTagString = "</part>";

//============================================================================
// Plugin Specific Helpers

// Parse the parameters inside a <part ...> tag and return the result.
//
// @return [may be null] {partName: ..., isHidden: ...}
//
var parseStartTagParams = function(paramText) {
	var params = paramText.readMacroParams();
	if (params.length == 0 || params[0].length == 0) return null;
	
	var name = params[0];
	var paramsIndex = 1;
	var hidden = false;
	if (paramsIndex < params.length) {
		hidden = params[paramsIndex] == "hidden";
		paramsIndex++;
	}
	
	return {
		partName: name, 
		isHidden: hidden
	};
}

// Returns the match to the next (end or start) part tag in the text, 
// starting the search at startIndex.
// 
// When no such tag is found null is returned, otherwise a "Match" is returned:
// [0]: full match
// [1]: matched "end" tag (or null when no end tag match)
// [2]: matched "start" tag (or null when no start tag match)
// [3]: content of start tag (or null if no start tag match)
//
var findNextPartEndOrStartTagMatch = function(text, startIndex) {
	var re = new RegExp(partEndOrStartTagRE);
	re.lastIndex = startIndex;
	var match = re.exec(text);
	return match;
}

//============================================================================
// Formatter

// Process the <part ...> ... </part> starting at (w.source, w.matchStart) for formatting.
//
// @return true if a complete part section (including the end tag) could be processed, false otherwise.
//
var handlePartSection = function(w) {
	var tagMatch = findNextPartEndOrStartTagMatch(w.source, w.matchStart);
	if (!tagMatch) return false;
	if (tagMatch.index != w.matchStart || !tagMatch[2]) return false;

	// Parse the start tag parameters
	var arguments = parseStartTagParams(tagMatch[3]);
	if (!arguments) return false;
	
	// Continue processing
	var startTagEndIndex = skipEmptyEndOfLine(w.source, tagMatch.index + tagMatch[0].length);
	var endMatch = findNextPartEndOrStartTagMatch(w.source, startTagEndIndex);
	if (endMatch && endMatch[1]) {
		if (!arguments.isHidden) {
			w.nextMatch = startTagEndIndex;
			w.subWikify(w.output,partEndTagREString);
		}
		w.nextMatch = skipEmptyEndOfLine(w.source, endMatch.index + endMatch[0].length);
		
		return true;
	}
	return false;
}

config.formatters.push( {
    name: "part",
    match: "<part\\s+[^>]+>",
	
	handler: function(w) {
		if (!handlePartSection(w)) {
			w.outputText(w.output,w.matchStart,w.matchStart+w.matchLength);
		}
	}
} )

//============================================================================
// Extend "fetchTiddler" functionality to also recognize "part"s of tiddlers 
// as tiddlers.

var currentParent = null; // used for the "." parent (e.g. in the "tiddler" macro)

// Return the match to the first <part ...> tag of the text that has the
// requrest partName.
//
// @return [may be null]
//
var findPartStartTagByName = function(text, partName) {
	var i = 0;
	
	while (true) {
		var tagMatch = findNextPartEndOrStartTagMatch(text, i);
		if (!tagMatch) return null;

		if (tagMatch[2]) {
			// Is start tag
	
			// Check the name
			var arguments = parseStartTagParams(tagMatch[3]);
			if (arguments && arguments.partName == partName) {
				return tagMatch;
			}
		}
		i = tagMatch.index+tagMatch[0].length;
	}
}

// Return the part "partName" of the given parentTiddler as a "readOnly" Tiddler 
// object, using fullName as the Tiddler's title. 
//
// All remaining properties of the new Tiddler (tags etc.) are inherited from 
// the parentTiddler.
// 
// @return [may be null]
//
var getPart = function(parentTiddler, partName, fullName) {
	var text = parentTiddler.text;
	var startTag = findPartStartTagByName(text, partName);
	if (!startTag) return null;
	
	var endIndexOfStartTag = skipEmptyEndOfLine(text, startTag.index+startTag[0].length);
	var indexOfEndTag = text.indexOf(partEndTagString, endIndexOfStartTag);

	if (indexOfEndTag >= 0) {
		var partTiddlerText = text.substring(endIndexOfStartTag,indexOfEndTag);
		var partTiddler = new Tiddler();
		partTiddler.set(
						fullName,
						partTiddlerText,
						parentTiddler.modifier,
						parentTiddler.modified,
						parentTiddler.tags,
						parentTiddler.created);
		partTiddler.abegoIsPartTiddler = true;
		return partTiddler;
	}
	
	return null;
}

// Hijack the store.fetchTiddler to recognize the "part" addresses.
//
var hijackFetchTiddler = function() {
	var oldFetchTiddler = store.fetchTiddler ;
	store.fetchTiddler = function(title) {
		var result = oldFetchTiddler.apply(this, arguments);
		if (!result && title) {
			var i = title.lastIndexOf('/');
			if (i > 0) {
				var parentName = title.substring(0, i);
				var partName = title.substring(i+1);
				var parent = (parentName == ".") 
						? store.resolveTiddler(currentParent)
						: oldFetchTiddler.apply(this, [parentName]);
				if (parent) {
					return getPart(parent, partName, parent.title+"/"+partName);
				}
			}
		}
		return result;	
	};
};

// for debugging the plugin is not loaded through the systemConfig mechanism but via a script tag. 
// At that point in the "store" is not yet defined. In that case hijackFetchTiddler through the restart function.
// Otherwise hijack now.
if (!store) {
	var oldRestartFunc = restart;
	window.restart = function() {
		hijackFetchTiddler();
		oldRestartFunc.apply(this,arguments);
	};
} else
	hijackFetchTiddler();




// The user must not edit a readOnly/partTiddler
//

config.commands.editTiddler.oldIsReadOnlyFunction = Tiddler.prototype.isReadOnly;

Tiddler.prototype.isReadOnly = function() {
	// Tiddler.isReadOnly was introduced with TW 2.0.6.
	// For older version we explicitly check the global readOnly flag
	if (config.commands.editTiddler.oldIsReadOnlyFunction) {
		if (config.commands.editTiddler.oldIsReadOnlyFunction.apply(this, arguments)) return true;
	} else {
		if (readOnly) return true;
	}

	return this.abegoIsPartTiddler;
}

config.commands.editTiddler.handler = function(event,src,title)
{
	var t = store.getTiddler(title);
	// Edit the tiddler if it either is not a tiddler (but a shadowTiddler)
	// or the tiddler is not readOnly
	if(!t || !t.abegoIsPartTiddler)
		{
		clearMessage();
		story.displayTiddler(null,title,DEFAULT_EDIT_TEMPLATE);
		story.focusTiddler(title,"text");
		return false;
		}
}

// To allow the "./partName" syntax in macros we need to hijack 
// the invokeMacro to define the "currentParent" while it is running.
// 
var oldInvokeMacro = window.invokeMacro;
function myInvokeMacro(place,macro,params,wikifier,tiddler) {
	var oldCurrentParent = currentParent;
	if (tiddler) currentParent = tiddler;
	try {
		oldInvokeMacro.apply(this, arguments);
	} finally {
		currentParent = oldCurrentParent;
	}
}
window.invokeMacro = myInvokeMacro;

// To correctly support the "./partName" syntax while refreshing we need to hijack 
// the config.refreshers.tiddlers to define the "currentParent" while it is running.
// 
(function() {
	var oldTiddlerRefresher= config.refreshers.tiddler;
	config.refreshers.tiddler = function(e,changeList) {
		var oldCurrentParent = currentParent;
		try {
			currentParent = e.getAttribute("tiddler");
			return oldTiddlerRefresher.apply(this,arguments);
		} finally {
			currentParent = oldCurrentParent;
		}
	};
})();

// Support "./partName" syntax inside <<tabs ...>> macro
(function() {
	var extendRelativeNames = function(e, title) {
		var nodes = e.getElementsByTagName("a");
		for(var i=0; i<nodes.length; i++) {
			var node = nodes[i];
			var s = node.getAttribute("content");
			if (s && s.indexOf("./") == 0)
				node.setAttribute("content",title+s.substr(1));
		}
	};
	var oldHandler = config.macros.tabs.handler;
	config.macros.tabs.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
		var result = oldHandler.apply(this,arguments);
		if (tiddler)
			extendRelativeNames(place, tiddler.title);
		return result;
	};
})();

// Scroll the anchor anchorName in the viewer of the given tiddler visible.
// When no tiddler is defined use the tiddler of the target given event is used.
window.scrollAnchorVisible = function(anchorName, tiddler, evt) {
	var tiddlerElem = null;
	if (tiddler) {
		tiddlerElem = document.getElementById(story.idPrefix + tiddler);
	}
	if (!tiddlerElem && evt) {
		var target = resolveTarget(evt);
		tiddlerElem = story.findContainingTiddler(target);
	}
	if (!tiddlerElem) return;

	var children = tiddlerElem.getElementsByTagName("a");
	for (var i = 0; i < children.length; i++) {
		var child = children[i];
		var name = child.getAttribute("name");
		if (name == anchorName) {
			var y = findPosY(child);
			window.scrollTo(0,y);
			return;
		}
	}
}

} // of "install only once"
//}}}

/***
<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>

!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2006 ([[www.abego-software.de|http://www.abego-software.de]])

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.

Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.

<html><sub><a href="javascript:;" onclick="scrollAnchorVisible('Top',null, event)">[Top]</sub></a></html>
***/
The code below shows how to create a controller and play a clip. You will need to pass in the name of your client application. See [[Suspending and Closing AppServers]] for an explanation of when an existing controller would be returned.

{{{
[C#]
bool isNewController = false;

// create a controller
ISimpleController icontroller = iappServer.CreateController(
"YourApplicationName", "C1", out isNewController);

// cast it to a player recorder and load a clip
ISimplePlayerRecorder player = (ISimplePlayerRecorder) icontroller;
player.Load("edl/cmf//local/V:/default/Clip");

// play for 3 seconds
player.Play();
Thread.Sleep(3000);

// eject the clip
player.Eject();

// close the channel
icontroller.CloseChannel();
}}}
{{{
[C++] 
short nIsNewController;
ISimpleControllerPtr spController;

// create a controller
HRESULT hr = spAppServer->CreateController(_bstr_t("YourApplicationName"), _bstr_t("C1"), 
 &nIsNewController, &spController);

// cast it to a player recorder and load a clip
ISimplePlayerRecorderPtr spPlayer = (ISimplePlayerRecorderPtr) spController;
hr = spPlayer->Load(_bstr_t("edl/cmf//local/V:/default/Clip"));

// play for 3 seconds
hr = spPlayer->Play();
Sleep(3000);

// eject the clip
hr = spPlayer->Eject();

// close the channel
hr = spController->CloseChannel();
}}}
On a Summit Client you have the ability to create a playlist with section and event mix effects transitions. Available playlist transition are:
* Events:
** video dissolve between events
** video fade-thru-matte color between events
** audio cross-fade between events
** audio fade-thru-silence between events
* Sections:
** video fade from matte color at start or end of section
** video fade to matte color at start or end of section
** audio fade up at start or end of section
** audio fade down at start or end of section
''@@color(red):Note:@@'' the Summit must have an ~AppCenter Pro license for this feature to work.

Full sample code can be downloaded from the [[CS-Script Sample Scripts]] section. Look for the sample named [[PlaylistMixEffects.cs|CS-Script/PlaylistMixEffects.cs]].

Below is a code snippet showing how to create a playlist with section and event mix effects. Please see [[Playlists sections and events]] and [[Create a basic playlist]] for more information on playlists.
{{{
[C#]
public enum MatteColor
{
	Black   = 0x108080,
	White   = 0xEB8080,
	Yellow  = 0xD68A10,
	Cyan    = 0xBC1099,
	Green   = 0xAC1A29,
	Magenta = 0x4EE5D6,
	Red     = 0x3EF066,
	Blue    = 0x2075F0
};

public enum VideoTransition
{
	None 		= 0,	// standard video cut
	Dissolve 	= 1,	// dissolve between 2 video tracks (available with both flying and playlist mix effects)
	FadeFromMatte 	= 2,	// fades from matte color to video (available with playlist section mix effects only)
	FadeToMatte 	= 3,	// fades from video to matte color (available with playlist section mix effects only)
	FadeThruMatte 	= 4,	// fades from video to matte color to video (available with both flying and playlist mix effects)
};

public enum AudioTransition 
{
	None 		= 0,	// standard audio cut
	CrossFade 	= 1,	// cross-fades between to audio tracks (available with both flying and playlist mix effects)
	FadeUp 		= 2,	// audio fade up (available with playlist section mix effects only)
	FadeDown 	= 3,	// audio fade down (available with playlist section mix effects only)
	FadeThruSilence = 4,	// fade from audio to silence to audio (available with both flying and playlist mix effects)
};

// turn off the player's loop play mode
player.LoopPlayMode = false;

// enable playlist pauses
player.SetChannelProperty("listpauses", "true");

// enable playlist repeats
player.SetChannelProperty("listrepeats", "true");

// set current bin            
player.SetCurrentBin(volume, bin);

// generate a unique name for the playlist
string playlist = player.GenerateUniqueName("playlist", "_");

// create the new playlist (which also loads it)
player.New(playlist);

// get an event editor
IEventEditor editor = (IEventEditor) player.GetEditor();		

// start the playlist at 1 hour
editor.SetStartingTimecode("01:00:00,00");

// create a new section at the end of the clip (i.e. "")
string section1 = editor.InsertSection("");

// setup section so that it starts by fading up from matte color
editor.SetSectionProperty(section1, "StartTrans", true);
editor.SetSectionProperty(section1, "StartTransDur", 120); 									// duration in fields
editor.SetSectionProperty(section1, "StartVidTrans", (int)VideoTransition.FadeFromMatte);	// video fade from matte
editor.SetSectionProperty(section1, "StartAudTrans", (int)AudioTransition.FadeUp);			// audio fade up
editor.SetSectionProperty(section1, "StartTransMatte", (int)MatteColor.Black);				// black matte color

// setup section so that it ends by fading down to matte color
editor.SetSectionProperty(section1, "EndTrans", true);
editor.SetSectionProperty(section1, "EndTransDur", 120); 								// duration in fields
editor.SetSectionProperty(section1, "EndVidTrans", (int)VideoTransition.FadeToMatte);	// video fade to matte
editor.SetSectionProperty(section1, "EndAudTrans", (int)AudioTransition.FadeDown);		// audio fade down
editor.SetSectionProperty(section1, "EndTransMatte", (int)MatteColor.Black);			// black matte color

// insert Clip 1 into section 1
string edit1 = editor.InsertPlayEvent(uriClip1, section1, 0, "");

// insert Clip 2 into section 1
string edit2 = editor.InsertPlayEvent(uriClip2, section1, 0, "");

// setup edit1 so that it fades thru matte color
editor.SetEventProperty(edit1, "Trans", true);
editor.SetEventProperty(edit1, "TransDur", 120);								// duration in fields
editor.SetEventProperty(edit1, "VidTrans", (int)VideoTransition.FadeThruMatte);	// fade thru matte
editor.SetEventProperty(edit1, "AudTrans", (int)AudioTransition.CrossFade); 	// audio crossfade
editor.SetEventProperty(edit1, "TransMatte", (int)MatteColor.Black); 			// matte color

// cue playlist to the start
player.CueStart();

// start playing
player.Play();
}}}
''[[Click here|Create a basic playlist]]'' for example code on how to create a basic playlist. See [[other code examples|Examples]], too.
[img[playlist diagram|images/playlist.png]]
''List:''
K2 play channels can also play a sequence of events called a play list. Events are the components that make up a play list.

''Section:''
All events in a playlist are contained in sections. Playlists must contain at least one section. A playlist can have up to 100 sections. A section can be configured to repeat or pause. 

''Event:''
Events are created by adding a clip or program to sections in a list. Each section can contain up to 1000 events. Events in a playlist can be added, removed or rearranged. 

Events can be renamed and trimmed. Trimming an event moves the mark-in and mark-out points. This only affects the event, not the source clip. 

Transitions between all events in a list is a cut (i.e. the last frame of an event is followed by the first frame of the next event).
Events can pause playout at their end. At event pauses, you can choose to show black, freeze on last frame, or freeze on next event.

''Program:''
Playlists can be saved as a program in the K2 Media Client. Programs created from a playlist include all the media and transitions in the playlist, but nothing that breaks the flow of playout, such as a pause at the end of an event. Programs are also created from the loop record mode.
txtMainTab=Timeline; txtMoreTab=Shadowed; chkSliderOptionsPanel=true; chkFETActions=true
As server systems become more complex, clips are ingested from a variety of sources. A VDCP playout controller cannot assume that a clip was ingested from a VDCP recorder with the same assumptions about timecode. Unfortunately the VDCP timecode model is limited and does not work for clips from all sources. The result is a cueing error. 

[[Download this document|zip/K2ProtocolTimecode.pdf]] to see how K2 software addresses this problem and maintains backward compatibility.

The document contains tips for setting the timecode for workflows where:
* You want the VDCP time sense values and cue values to match timecode recorded from a LTC or VITC source.
* You want your VDCP player to be able to cue material that was recorded from a variety of sources, not just VDCP.
* You have a controller database that has stored SOM values from a previous K2 release.
* You want VDCP to work like it did on Profile.
* You want to cue using a clip that has striped timecode.
<<formTiddler [[ChangeNoticeTemplate]]>><data>{"date":"2008-01-24","description":"Removing hard coded volume information. Allows emulator to support protocols. ","assocCRs":"87649","buildsWithFix":"K2 3.2.58.831","checkinTimeBranch":"1/24/2008 11:40 AM - K2_maint_4\n2/1/2008 1:19 PM - Main\n"}</data>
If you have any questions about how to use the K2 ~AppServer API or how to best perform a specific task, please feel free to contact us at: ''[[GVDeveloperSupport@thomson.net|mailto:GVDeveloperSupport@thomson.net?subject=K2 AppServer question]]''
Below is sample code showing how to read messages from the K2's log file.

{{{
[C#]
// get a log object from the AppServer
IAppServerLog3 log = (IAppServerLog3) iappServer.CreateLog(appName);

// open the K2's log file
log.Open("C:\\logs\\mlog.mmf");			

// set the format to get strings out rather than binary data
log.ReadFormat = "string";
int actualCount = 0;	// var used to tell number objects returned


// get the first 10 messages from the top of the log
object[] firstTen = (object[]) log.ReadNextMsg(10, ref actualCount);
foreach (object o in firstTen)
{	
	Console.WriteLine(o);
}
Console.WriteLine("");


// get the last three messages from the bottom of the log
object[] lastThree = (object[]) log.ReadLastMsg(3, ref actualCount);
foreach (object o in lastThree)
{
	Console.WriteLine(o);
}
Console.WriteLine("");


// get the three messages before that
object[] prevThree = (object[]) log.ReadPrevMsg(3, ref actualCount);
foreach (object o in prevThree)
{
	Console.WriteLine(o);
}

// delete all messages in log
// log.Clear();			

// close the log file
log.Close();
}}}
Below is code showing how to record a 5 second clip:
# A clip with a unique name is first generated in the V:\default bin. 
# Next, the clip is cued on the recorder at the start of the clip. 
# The recording length is set to 5 seconds (note: recording length can be given as a timecode string or a long number of fields). 
# Recording is then started and 10 seconds later the clip is ejected. 
You can view the clip's properties in ~AppCenter's ~MediaMonitor.

{{{
[C#]
bool isNewController = false;

// create a controller
ISimpleController icontroller = iappServer.CreateController(
"YourApplicationName", "C1", out isNewController);

// cast the controller to an ISimplePlayerRecorder interface.
ISimplePlayerRecorder recorder = (ISimplePlayerRecorder) icontroller;

// generate a new clip name in the V:\default bin
recorder.SetCurrentBin("V:", "default");
string newclip = recorder.GenerateUniqueName("test", "_");

// create the new clip, cue the record, and set the record length to 5 seconds
recorder.New(newclip);
recorder.CueRecord(0);
recorder.SetRecordLength("00:00:05.00");
recorder.Record();

// wait for 10 seconds only to show the record stopping at 5 seconds
Thread.Sleep(10000);

// eject clip and close channel
recorder.Eject();
icontroller.CloseChannel();
}}}
{{{
[C++]
short nIsNewController;
ISimpleControllerPtr spController;
_variant_t varChanNum(m_bstrChannel);
 
// create the controller
HRESULT hr = spAppServer->CreateController(m_bstrAppName, varChanNum, 
 &nIsNewController, &spController);

// generate a new clip name in the V:\default bin
spController->SetCurrentBin(_bstr_t("V:"), _bstr_t("default"));
BSTR bstrNewClip;
hr = spController->GenerateUniqueName(_bstr_t("test"), _bstr_t("_"), &bstrNewClip);

// cast the controller to a recorder
ISimplePlayerRecorderPtr spRecorder = (ISimplePlayerRecorderPtr) spController;

// create the new clip, cue the record, and set the record length to 10 seconds
spRecorder->New(bstrNewClip);
spRecorder->CueRecord(0);
spRecorder->SetRecordLength(_bstr_t("00:00:05.00"));
spRecorder->Record();

// wait for 10 seconds only to show the record stopping at 5 seconds
Sleep(10000);

// eject clip and close channel
hr = spRecorder->Eject();
hr = spController->CloseChannel();
}}}
K2 ~AppServer Developer's Guide [img[rss|images/rss.jpg]] RSS feed is available [[here|K2DevGuide.xml]].
You can download zipped up sample C# and C++ projects by clicking on the links below. The projects contain DOS console applications that connect to and control a K2 using 3.2.74.1000 client ~DLLs.

''Visual Studio 2003''
[[C# K2 Client|zip/VS2003_CSharp_K2Client.zip]]
[[C++ K2 Client|zip/VS2003_Cpp_K2Client.zip]]

''Visual Studio 2005''
[[C# K2 Client|zip/VS2005_CSharp_K2Client.zip]]
[[C++ K2 Client|zip/VS2005_Cpp_K2Client.zip]]

''Visual Studio 2008''
[[C# K2 Client|zip/VS2008_CSharp_K2Client.zip]]
[[C++ K2 Client|zip/VS2008_Cpp_K2Client.zip]]

Also try [[clicking here to see the C# Sample Scripts|CS-Script Sample Scripts]] section for many C# samples that run from a single file.

In general, you set the aspect ratio conversion in ~AppCenter's System Configuration panel. There you can select the aspect ratio conversion type on a channel-by-channel basis.

Additionally, you can override the Aspect Ratio Conversion on a clip-by-clip basis. You do this by setting the "~AspectRatioConversion" property of the asset to one of these strings:
* "Default"
* "Bars"
* "Halfbars"
* "Crop"
* "Stretch"
Full sample code can be downloaded from the [[CS-Script Sample Scripts]] section. Look for the sample named [[SetAssetARC.cs|CS-Script/SetAssetARC.cs]].

More asset property information can be found here:
* [[Get asset properties]]
* [[Asset properties]]

Below is a code snippet demonstrating how to get an editor for an asset and set the ~AspectRatioConversion property:
{{{
[C#]
string asset = "edl/cmf//local/V:/default/Clip";

// get a media mgr object
using ( IMediaMgr mediaMgr = iappServer.CreateMediaMgr(appName) )
{
	// get an editor for the asset
	using ( ISimpleEditor2 editor = (ISimpleEditor2) mediaMgr.CreateEditor(asset) )
	{
		// set property
		editor.SetProperty("AspectRatioConversion", "Default");
		
		// get it back just to verify. You can check in AppCenter, too.
		string assetType = (string) editor.GetProperty("AspectRatioConversion");
		Console.WriteLine("ARC = " + assetType);
		
		// Make sure you detach your editor! if not, you'll leave an open reference to your movie.
		editor.Detach();
	}
}
}}}
/***
|''Name:''|SettingsPlugin|
|''Version:''|1.1.0 (2006-07-30)|
|''Type:''|plugin|
|''Source:''|http://tiddlywiki.abego-software.de/#SettingsPlugin|
|''Author:''|Udo Borkowski (ub [at] abego-software [dot] de)|
|''Documentation:''|[[SettingsPlugin Documentation]]|
|''~SourceCode:''|[[SettingsPlugin SourceCode]]|
|''Licence:''|[[BSD open source license (abego Software)]]|
|''~CoreVersion:''|2.0.7|
|''Browser:''|Firefox 1.5.0.2 or better; Internet Explorer 6.0|
^^This tiddler contains compressed source code. [[Full Source Code|SettingsPlugin SourceCode]].^^
***/
// /%
if(!version.extensions.SettingsPlugin){if(version.major<2){(function(){var s="Use TiddlyWiki 2.0 or better to run the Settings Plugin.";alert(s);throw s;})();}version.extensions.SettingsPlugin={major:1,minor:1,revision:0,date:new Date(2006,6,30),type:"plugin",source:"http://tiddlywiki.abego-software.de/#SettingsPlugin",documentation:"[[SettingsPlugin Documentation]]",sourcecode:"[[SettingsPlugin SourceCode]]",author:"Udo Borkowski (ub [at] abego-software [dot] de)",licence:"[[BSD open source license (abego Software)]]",coreVersion:"2.0.7",browser:"Firefox 1.5.0.2 or better; Internet Explorer 6.0"};if(!window.abego){window.abego={};}if(!abego.setTiddlerText){abego.setTiddlerText=function(_2,_3,_4,_5,_6,_7){var _8=_2.getTiddler(_3);if(_8&&(_8.text==_4)){return;}var _9=config.options.chkForceMinorUpdate;var _a=!_8?_5:_8.tags;_2.suspendNotifications();try{_2.saveTiddler(_3,_3,_4,_9?undefined:config.options.txtUserName,_9?undefined:new Date(),_a);}finally{_2.resumeNotifications();}if(_7){_2.notify(_3,true);}if(!_6&&config.options.chkAutoSave){saveChanges();}};}(function(){var _b="PrivateSettings";var _c="chkUsePrivateSettings";var _d="chkMakeSettingPrivateWhenChanged";var _e=null;var _f=false;var _10=function(_11,s){var _13=s.split(";");var _14={};for(var c=0;c<_13.length;c++){var p=_13[c].indexOf("=");if(p!=-1){var _17=_13[c].substr(0,p).trim();var _18=_13[c].substr(p+1).trim();_11[_17]=unescape(_18);_14[_17]=true;}}return _14;};var _19=function(_1a){var s=store.getTiddlerText(_b);_e=s?_10(_1a,s):{};};var _1c=function(_1d){_10(_1d,document.cookie);};var _1e=function(){var _1f={};_1c(_1f);return _1f;};var _20=function(){var _21={};_19(_21);return _21;};var _22=function(_23,_24){document.cookie=_23+"="+escape(_24)+"; expires=Fri, 1 Jan 2038 12:00:00 UTC; path=/";};var _25=function(_26,s,_28){var _29=_20();if(s===null){if(!_e[_26]){return;}delete _29[_26];delete _e[_26];}else{var _2a=_29[_26];if(_2a!==undefined&&_2a==s){return;}_29[_26]=s;_e[_26]=true;}var t="";for(var i in _29){if(t){t+="; ";}t+=i+"="+escape(_29[i]);}abego.setTiddlerText(store,_b,t,["excludeLists","excludeSearch"],_28);if(!_28&&config.options.chkAutoSave){saveChanges();}};config.shadowTiddlers["BSD open source license (abego Software)"]="See [[Licence|http://tiddlywiki.abego-software.de/#%5B%5BBSD%20open%20source%20license%5D%5D]].";config.shadowTiddlers["SettingsPlugin Documentation"]="[[Documentation on abego Software website|http://tiddlywiki.abego-software.de/#%5B%5BSettingsPlugin%20Documentation%5D%5D]].\n\n^^You may copy the documentation tiddler from the website to your TiddlyWiki.\nThen you don't need to access the internet to read the documentation.^^";config.shadowTiddlers["SettingsPlugin SourceCode"]="Rightclick this [[link|http://tiddlywiki.abego-software.de/src/Plugin-Settings-src.js]] and choose 'Save target/link as...' to get the plugin source code from the abego Software website.";config.shadowTiddlers["Show Settings"]="<<showSettings>>";if(config.options[_c]===undefined){config.options[_c]=false;}if(config.options[_d]===undefined){config.options[_d]=false;}config.shadowTiddlers.AdvancedOptions+="\n''Private Settings: ''<<option "+_c+">> Use private settings.  <<option "+_d+">> Make setting private when changed.&#160;&#160;&#160;[[Show Settings]].\n^^(Private settings are stored in this ~TiddlyWiki, shared settings are stored as cookies. For more information see the [[Settings documentation|SettingsPlugin Documentation]].)^^";abego.usePrivateSettings=function(){return _f;};abego.setUsePrivateSettings=function(f){if(f!=abego.usePrivateSettings()){_f=f;loadOptionsCookie();}};abego.isUsePrivateSettingsOption=function(_2e){return _2e==_c;};abego.makeSettingPrivateWhenChanged=function(){return config.options[_d];};abego.setMakeSettingPrivateWhenChanged=function(f){config.options[_d]=f;};abego.getSettings=function(){var _30={};_1c(_30);_f=_30[_c]=="true";if(abego.usePrivateSettings()){_19(_30);}return _30;};abego.getSetting=function(_31){var s=abego.getSettings()[_31];return !s?"":s;};abego.saveSetting=function(_33,_34){if(abego.isUsePrivateSettingsOption(_33)){_22(_33,_34);abego.setUsePrivateSettings(_34=="true");loadOptionsCookie();window.alert("You changed the 'Use private settings' option.\nPlease reload your TiddlyWiki to update the settings.\n");return;}if(abego.usePrivateSettings()&&(abego.isSettingPrivate(_33)||abego.makeSettingPrivateWhenChanged())){_25(_33,_34);}else{_22(_33,_34);}};abego.isSettingPrivate=function(_35){if(abego.isUsePrivateSettingsOption(_35)){return false;}if(!_e){_20();}return !!_e[_35];};abego.isPasswordSetting=function(_36){return (_36.substr(0,3)=="pas")||(_36.substr(0,6)=="chkpas");};abego.makeSettingPrivate=function(_37,_38,_39){if(abego.isPasswordSetting(_37)){_25(_37,null,_39);return;}var _3a=_20();var _3b=_3a[_37]!==undefined;if(_38==_3b){return;}var _3c=_1e();if(_38){_25(_37,_3c[_37],_39);}else{if(_3c[_37]===undefined){_22(_37,_3a[_37]);}_25(_37,null,_39);}};abego.setAllSettingsPrivate=function(_3d,_3e){var _3f=abego.getSettings();for(var s in _3f){abego.makeSettingPrivate(s,_3d,true);}if(config.options.chkAutoSave){saveChanges();}if(_3e){story.refreshTiddler(_3e,1,true);}return false;};})();config.macros.showSettings={label:"showSettings",prompt:"Display the current TiddlyWiki settings"};config.macros.showSettings.handler=function(_41,_42,_43,_44,_45,_46){var _47=function(a){var s=a.substr(0,3);return s=="txt"||s=="chk";};var _4a=function(a,b){var s1=a.toLowerCase();var s2=b.toLowerCase();return (s1<s2)?-1:(s1==s2)?0:1;};var _4f=function(a,b){var _52=_47(a);var _53=_47(b);if(_52){if(!_53){return -1;}else{return _4a(a.substr(3),b.substr(3));}}else{if(_53){return 1;}else{return _4a(a,b);}}};var s="<html><table><tr align=\"left\"><th>Private</th><th>Name</th><th>Value</th></tr>";var c=abego.getSettings();var _56=[];for(var i in c){if(!abego.isPasswordSetting(i)){_56.push(i);}}_56.sort(_4f);for(i=0;i<_56.length;i++){var _58=_56[i];var _59=_47(_58)?_58.substr(3)+" ("+_58.substr(0,3)+"...)":_58;var _5a=abego.isUsePrivateSettingsOption(_58)?"":"<input name=\""+_58+"\" type=\"checkbox\" onclick=\"abego.onPrivateSettingClick(this)\""+(abego.isSettingPrivate(_58)?" checked":"")+"/>";s+="<tr><td align=\"right\">"+_5a+"</td><td>"+_59+"</td><td>"+c[_58]+"</td></tr>\n";}s+="</table>";s+="<a class=\"button\" title=\"Make all current settings private\" href=\"javascript:;\" onclick=\"abego.setAllSettingsPrivate(true,'"+_46.title+"',1);\">Make all private</a>";s+="<a class=\"button\" title=\"Make all current settings shared\" href=\"javascript:;\" onclick=\"abego.setAllSettingsPrivate(false,'"+_46.title+"',1);\">Make all shared</a>";s+="</html>";wikify(s,_41);};abego.onPrivateSettingClick=function(_5b){abego.makeSettingPrivate(_5b.name,_5b.checked);return false;};this.loadOptionsCookie=function(){if(safeMode){return;}var _5c=abego.getSettings();for(var _5d in _5c){var _5e=_5c[_5d];switch(_5d.substr(0,3)){case "txt":config.options[_5d]=unescape(_5e);break;case "chk":config.options[_5d]=_5e=="true";break;}}};this.saveOptionCookie=function(_5f){if(safeMode){return;}var v="";switch(_5f.substr(0,3)){case "txt":v=config.options[_5f].toString();break;case "chk":v=config.options[_5f]?"true":"false";break;}abego.saveSetting(_5f,v);};loadOptionsCookie();}
// %/

For SettingsPlugin source code see the [[archive|http://tiddlywiki.abego-software.de/archive]].
<<closeAll>><<permaview>><<newTiddler>><<newTiddlerWithForm ChangeNoticeTemplate "new change notice" '"<enter synopsis here>"' askUser>><<saveChanges>><script>
var onClick = function() {
story.closeAllTiddlers();
restart();
// displayTiddler(null,'About',1,null,null,false,false);
}
createTiddlyButton(place,"reset","Reset (Shortcut: Alt-T)",onClick, null, null, "T");
</script><script>var btn = createTiddlyButton(place,"configuration","Configure this TiddlyWiki",onClickTiddlerLink,"button");btn.setAttribute("tiddlyLink","Configuration");</script><<slider chkSliderOptionsPanel OptionsPanel 'options »' 'Change TiddlyWiki advanced options'>>
© 2009 Grass Valley
K2 ~AppServer Developer's Guide
http://www.gvgdevelopers.com/K2DevGuide/K2DevGuide.html
/***
!Colors Used
*@@bgcolor(#ffb200): #ffb200 - Primary Color@@
*@@bgcolor(#ffcc66): #ffcc66 - Primary Color Light@@
*@@bgcolor(#ff8000): #ff8000- Primary Color Bright@@
*@@bgcolor(#b75b00): #b75b00 - Primary Color Dark@@
*@@bgcolor(#666666): #666666 - Text Color Light@@
*@@bgcolor(#703):color(#fff): #703 - Title Color @@
***/
/*{{{*/
.header {
}

.headerForeground {
 background-image: url(images/gv_background.jpg);
 color: #FFF;
}
.headerForeground a {
 color: #FFF;
}

a {
 color: #20390D;
}

a:hover{
 color: #20390D;
}

#sidebarOptions input {
 border: 1px solid #912100;
}

#sidebarOptions .sliderPanel {
 background: #EEEEEE;
}

#sidebarOptions .sliderPanel a {
 color: #20390D;
}

#sidebarOptions .sliderPanel a:hover {
 color: #912100;
}

#sidebarOptions .sliderPanel a:active {
 color: #912100;
 background: #fff;
}
h1,h2,h3,h4,h5 {
 color: #20390D;
 background: #EEEEEE;
}

/*}}}*/
/***
!Other Styling
***/
/*{{{*/
.headerShadow {
 visibility:hidden;
}
.headerShadow a {
 visibility:hidden;
}
.headerShadow {
 padding: 0.3em 0em 1em 1em;
}

.headerForeground {
 padding: 0.3em 0em 1em 1em;
}

a {
 font-family: Arial, Helvetica, sans-serif;
 text-decoration: none;
}

a:hover{
 text-decoration: underline;
 background: none;
}

.button:hover {
 text-decoration: none;
}

.abegoLink .externalLink {
 font-weight: bold;
 text-decoration: none;
}

.abegoLink .externalLink:hover {
 font-weight: bold;
 text-decoration: underline;
}

#mainMenu .tiddlyLinkExisting, 
#mainMenu .tiddlyLinkNonExisting{
 font-weight: bold;
 font-style: normal;
}

#sidebarOptions .sliderPanel a {
 color: #20390D;
}

#sidebarOptions .sliderPanel a:hover {
 background:none;
}

/*}}}*/
Updated: May 28, 2009

The Summit Emulator is available for download here.

It is recommended that you install the Summit Emulator on a dedicated machine or a virtual machine such as [[Microsoft's Virtual PC|http://www.microsoft.com/windows/products/winfamily/virtualpc/default.mspx]]. The reason for this is to not conflict other Grass Valley software that you may have installed or other components that you may have installed for other development efforts.

''Download:''
[[Click here to download the Summit Emulator Prerequisites Installer|zip/Summit Emulator PreRequisites.zip]] - Size: 152 MB
[[Click here to download the Summit Emulator|zip/SummitEmulator_v7.0.11.1101.zip]] - Size: 39 MB - version 7.0.11.1101 updated May 28, 2009

Test Clips are also available for downloading and importing into the Summit Emulator. [[Click here to download them.|Test Clips]]
<br/>
''Requirements:''
The PC requirements for the Summit Emulator are the same as the [[ControlPoint PC System Requirements. Click here to see them|ControlPoint PC System Requirements]]. 
<br/>
''Functionality:''
Click here on [[K2 Summit information and API additions]] to see what new functionality Summit offers.

The Summit Emulator is the full Summit software with the hardware codec portion emulated. That means:
* You can load a clip in ~AppCenter and press play. The application will show the clip playing and the time code will progress, but since there is not a real decoder, you cannot see the output displayed anywhere.
* You can record a "fake: clip. You will see the recording growing in length. It will not have a thumbnail since there is no real video to decode.
* Transfers and FTP are supported.
* All ~APIs are supported - AMP, BVW, VDCP, and ~AppServer over both Ethernet and ~RS422 connections.
* ~RS422 protocol control has been successfully tested using a ~RocketPort Plus 2P SMPTE RS422 PCI card. However, a USB Digi Edgeport/4 ~RS422 converter box was not recognized by the emulator.
<br/>
''Licensing:''
In the emulator, the K2 Client software and ~AppCenter Pro software have a default 30-day trial license. If you want to extend this or want to add ~DV100 support, please contact ''[[GVDeveloperSupport@thomson.net|mailto:GVDeveloperSupport@thomson.net?subject=Emulator license]]'' about extending or adding licenses.
<br/>
''Installation Steps:''
1. First, install the Summit Emulator Prerequisites (download from link above). Unzip the prerequisites zip file and run "setup.exe".

2. Next, install the Summit Emulator (download from link above). Unzip the emulator zip file and run "setup.exe". After the emulator is installed it will ask to reboot your system. Say Yes.
<br/>
''Starting:''
1. Click Start / Run and type "log". This starts the K2 log viewer so that you can see the services starting up.
2. On the desktop, double-click "~WinRtpMgr". This starts the K2 services.
3. Watch the the log viewer for startup messages. Be patient.
4. If you do not have an ~RS422 serial card connected you will likely see "invalid serial port" messages. This is normal and expected.
5. Look for messages that all controllers (channels) are created. At this point the services are up and running.
6. Start ~AppCenter either from the ~WinRtpMgr icon or the desktop.
7. Login using an administrator username and password that you have setup for your system.
<br/>
''Limitations:''
* Imported MXF files do not show thumbnails of the clips correctly. Imported GXF files do not have this problem.
* Striping clips does not work in the emulator. It depends on a lower level driver that is not present in the emulator.
* In ~AppCenter in a channel's Option / Timecode panel, if you choose any of the “Recorded Timecode” choices, the value that will be used is just a simulated timecode that is ever-increasing.  The reason for this is that on a real K2, timecode information is provided from hardware (and that hardware interface is only simulated by the emulator).  However, if you choose the “Generated Timecode” option in the emulator, when playing back a clip you will get a timecode that is relative to a clip starting point of 00:00:00,00.  On a real K2 of course, all timecode modes are supported, and if you choose “system clock” as the time of day source, and select the “Time of Day” timecode mode, you would be able to record clips using the system clock as their timecode source.”
* Scheduled context changes are not implemented in the K2 Emulator. On a real K2 Client these are messages we pass to the real-time system telling it to do something at a specified timecode (e.g. start playing at timecode "xx:xx:xx.xx"). Pre-roll commands in protocols are also implemented using scheduled context changes. So for testing purposes with the emulator, you can still watch the log output to see that a "Play" command is received.
<br/>
''Troubleshooting:''
1. __Cannot login to ~AppCenter or cannot see and control channels in the emulator__:
When ~AppCenter starts it checks to see if a user has administrator rights before granting access to channels. It is checking for administrator rights by looking for one of these conditions:

a. user is part of the group "BUILTIN\\Administrators" (this works for US versions of Windows XP), or
b. user is part of the group "<machinename>\~K2Administrators". 

If you are using a non-US version of Windows XP, there are two ways of resolving this problem:

''First option:''
- Change all of your Windows "Regional and language options" to "US / English"
- add your username to the "BUILTIN\Administrators" group
- reboot
- login to ~AppCenter with username.

''Second option:''
- create a local group name on the emulator machine called "~K2Administrators". 
- create a new local user on the same machine and make them a member of the "~K2Administrators" group. 
- restart ~AppCenter and login with the new username.
~AppService is designed so that when a client application exits it can either:

1) shutdown all of the resources it acquired, or 
2) leave the resources up-and-running so that it can reacquire the same resources the next time the application runs

One use for this would be an application that keeps a player or recorder running even when the application is not running. This feature is implemented by calling the ~CloseConnection or ~SuspendConnection functions on the ~AppServer object. 

@@color(red):Note: it is up to the application to manage the behavior of these resources. If an application exits and leaves a recorder suspended in a recording state, the recorder will continue to use disk space until it either uses up the rest of the drive space or until the application reconnects and tells it to stop.@@

Note: ~AppServers will not remain suspended if the ~AppService is restarted or if the K2 is rebooted.

''Reaquiring a Suspended ~AppServer:''
To reacquire a suspended ~AppServer, call ~CreateAppServer on the ~AppServerMgrProxy passing in the same suite string as before. 

~AppServerMgrProxy method:
{{{
public IAppServer CreateAppServer(string suiteName, string app,
 out bool isNewConnection);
}}}
~AppService will then find the matching ~AppServer object, set its state back to "active", and return it. If the ~AppServer object returned was previously suspended, the variable "isNewConnection" will return with a FALSE value. If ~AppService creates a new ~AppServer object, the variable "isNewConnection" will return with a TRUE value.

''Reaquiring a Suspended Controller:''
To reaquire a suspended controller, call ~CreateController on the ~AppServer object. If the channel was previously suspended, the variable "isNewConnection" will return with a FALSE value. If the ~AppServer creates a new Controller object, the variable "isNewConnection" will return with a TRUE value.
 
~AppServer method:
{{{
public ISimpleController CreateController(string app, object channelId, 
 out bool isNewConnection);
}}}
 
Note: remember that channels can be stolen by other applications. So even though you suspended your ~AppServer, until the time that your application reconnects, other applications may steal the channel away from your suspended ~AppServer. So check the value of the "isNewConnection" variable to make sure that you are really getting back the controller that you previously had.
If you have a routine that is repeatedly making calls that are, for example, looking for a status change, make sure that you allow enough time between calls for something to happen. If you write a tight loop with no delay between calls, both your client application and the K2 server will become so busy setting up and tearing down calls that ~AppService won't have time to do the work that you are waiting on. Additionally, CPU usage of both application will go up dramatically and the client may have so many calls queued up that it runs out of worker threads to handle all of the calls.

Instead, consider how much time is appropriate between calls and make sure that your routine waits that amount between calls.

Additionally, see [[here|Get multiple properties]] to see how you can combine multiple property calls into one property call.
You can download the following GXF test clips for your system. Use ~AppCenter's "Clip / Import" menu to import these.

[[K2_525_D10_colorbar.gxf|Clips/K2_525_D10_colorbar.gxf]] - size: 20 MB
[[K2_525_DV25_colorbar.gxf|Clips/K2_525_DV25_colorbar.gxf]] - size: 20 MB
[[K2_525_DV50_colorbar.gxf|Clips/K2_525_DV50_colorbar.gxf]] - size: 37 MB
[[K2_525_IFrame_colorbar.gxf|Clips/K2_525_IFrame_colorbar.gxf]] - size: 13 MB
[[K2_525_LGOP_colorbar.gxf|Clips/K2_525_LGOP_colorbar.gxf]] - size: 10 MB
[[K2_525_MPEG_GOP.gxf|Clips/K2_525_MPEG_GOP.gxf]] - size: 8 MB
[[K2_525_MPEG_IFrame.gxf|Clips/K2_525_MPEG_IFrame.gxf]] - size: 10 MB
[[K2_525_LGOP_colorbar_CC.gxf|Clips/K2_525_LGOP_colorbar_CC.gxf]] - size: 12 MB
[[525_DV100_720p_colorbar.gxf|Clips/525_DV100_720p_colorbar.gxf]] - size: 160 MB
[[525_DV100_1080i_colorbar.gxf|Clips/525_DV100_1080i_colorbar.gxf]] - size: 160 MB

[[K2_625_D10_colorbar.gxf|Clips/K2_625_D10_colorbar.gxf]] - size: 20 MB
[[K2_625_DV25_colorbar.gxf|Clips/K2_625_DV25_colorbar.gxf]] - size: 20 MB
[[K2_625_DV50_colorbar.gxf|Clips/K2_625_DV50_colorbar.gxf]] - size: 37 MB
[[K2_625_IFrame_colorbar.gxf|Clips/K2_625_IFrame_colorbar.gxf]] - size: 13 MB
[[K2_625_LGOP_colorbar.gxf|Clips/K2_625_LGOP_colorbar.gxf]] - size: 10 MB
[[K2_625_MPEG_GOP.gxf|Clips/K2_625_MPEG_GOP.gxf]] - size: 8 MB
[[K2_625_MPEG_IFrame.gxf|Clips/K2_625_MPEG_IFrame.gxf]] - size: 13 MB
[[625_DV100_720p_colorbar.gxf|Clips/625_DV100_720p_colorbar.gxf]] - size: 160 MB
[[625_DV100_1080i_colorbar.gxf|Clips/625_DV100_1080i_colorbar.gxf]] - size: 160 MB


[[525_DV100_1080i_CC.gxf|Clips/525_DV100_1080i_CC.gxf]] - size: 70 MB (NOTE: closed captioning does not match audio in clip, but should be useful for test purposes.)
[img[AppService class diagram|images/AppServiceClassDiagram.png]]
''K2 ~AppService class diagram''

''~AppService and the ~AppServerMgr object:''
A service runs on the K2 called the "Grass Valley ~AppService". Its job is to provide clients with a way to control or retrieve information from the K2. ~AppService is implemented by a singleton ~AppServerMgr object on the K2 whose job is to create and manage ~AppServer objects for client applications. A client application first connects to the ~AppServerMgr, then asks the ~AppServerMgr to create an ~AppServer for it.

''~AppServer:''
An ~AppServer is an object that is used for creating K2 subsystem objects. These subsystem objects allow you to control or get information from various sections of K2. For example, a ~MediaMgr object provides file information. A Control object provides ways to record or play clips, etc. 

The [[AppServer|API/GrassValley.Mseries.AppServer.AppServerProxy.html]] keeps track of all objects that have been created. When an ~AppServer is closed, all of its subsystem objects are closed as well. If a client application would like to stop, but still keep all of its resources running, it can suspend the ~AppServer instead. This allows the application to start again later and reconnect to the same ~AppServer. This is useful if you want to keep a player or a recorder running even when the application is stopped.

''K2 Subsystem objects:''
These are the different types of subsystem objects that an ~AppServer can create:
* [[AppServerLog|API/GrassValley.Mseries.AppServerLog.AppServerLogProxy.html]] - for writing messages to the K2's log
* [[ChanStatus|API/GrassValley.Mseries.ChanStatus.ChanStatusProxy.html]] - for getting status information about channels
* [[ConfigMgr|API/GrassValley.Mseries.ConfigMgr.ConfigMgrProxy.html]] - for getting configuration information from the K2
* [[Control|API/GrassValley.Mseries.Control.ControlProxy.html]] - for controlling players and recorders
* [[DVCapture|API/GrassValley.Mseries.DVCapture.DVCaptureProxy.html]] - for capturing material from a DV source
* [[Editor|API/GrassValley.Mseries.Editor.EditorProxy.html]] - for editting assets
* [[MediaMgr|API/GrassValley.Mseries.MediaMgr.MediaMgrProxy.html]] - for getting information about volumes, bins, and assets
* [[Status|API/GrassValley.Mseries.Status.html]] - for getting status information from the K2
* [[TransferQueue|API/GrassValley.Mseries.TransferQueue.TransferQueueProxy.html]] - for transferring assets between K2s, ~M-Series, and Profiles
/***
|''Name:''|TiddlerWithParamsPlugin|
|''Version:''|1.0.1 (2006-03-22)|
|''Source:''|http://tiddlywiki.abego-software.de/#TiddlerWithParamsPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license]]|
!Description

The TiddlerWithParamsPlugin extends the build-in {{{<<tiddler...>>}}} macro. It replaces placeholders ($1, $2, ...) in the given tiddler by values passed with the macro. Then it inserts the (replaced) text, just like the original {{{<<tiddler...>>}}} macro.

''Syntax:'' 
|>|{{{<<}}}''tiddler '' //tiddlerName// [//className//] [''asText''] [''with:'' //arguments// ] [''prefix:'' //prefixString//] {{{>>}}}|
|//tiddlerName//|The name of the tiddler to be included. The tiddler may contain placeholders ($1, $2, ... $9) that will be replaced with the values passed with the macro|
|//className//|The (CSS) class to be used around the embedded tiddler|
|''asText''|When defined the (replaced) content of the tiddler is inserted as pure text, i.e. it is not "wikified".|
|//arguments//|up to 9 arguments may be passed to the macro, used as the values for the placeholders $1, $2, ... $9 in the referenced template|
|//prefixString//|By default the placeholders $1, $2, $3,..., $9 are used. But you may change the "prefix" before the placeholder number ("$") to some other text through the "prefix:" option. This may be necessary when you are using the $n in the tiddler you are referencing (e.g. when you are using regular expressions).|
|>|~~Syntax formatting: Keywords in ''bold'', optional parts in [...]. ~~|

!Example

The following ''//ProjectTemplate//'' tiddler defines an "Overview" page for a project, that gives access to various "sub-tiddlers" used in the project, that follow a static naming schema (e.g. all Notes for any project are stored in a tiddler called "//projectName// Notes"). $1 holds the name of the Project (e.g. "ForEachTiddler"), $2 holds the type of the published component (e.g. "Plugin", "Macro", "Function").
{{{
![[$1Project]] Overview
* [[$1$2]]
* [[ToDo|$1 ToDos]]
* [[Notes|$1 Notes]]
* [[Examples|$1Examples]]
* [[Tests|$1 Tests]]
* [[Open Bugs/CRs|$1 Open Bugs and ChangeRequests]]
}}}

This template is now used in the ''//ForEachTiddlerProject//'' tiddler:
{{{
<<tiddler ProjectTemplate with: ForEachTiddler Plugin>>
}}}

This results in the following tiddler text for the ''//ForEachTiddlerProject//'' tiddler:
{{{
![[ForEachTiddlerProject]] Overview
* [[ForEachTiddlerPlugin]]
* [[ToDo|ForEachTiddler ToDos]]
* [[Notes|ForEachTiddler Notes]]
* [[Examples|ForEachTiddlerExamples]]
* [[Tests|ForEachTiddler Tests]]
* [[Open Bugs/CRs|ForEachTiddler Open Bugs and ChangeRequests]]
}}}

!Revision history
* v1.0.0 (2006-01-20)
** initial version
* v1.0.1 (2006-03-22)
** Added 'asText' option
** Support Safari (Thanks to Elise Springer for reporting the problem)
!Code
***/
//{{{
//============================================================================
// TiddlerWithParamsPlugin
//============================================================================

// Ensure that the Plugin is only installed once.
//
if (!version.extensions.TiddlerWithParamsPlugin) {

version.extensions.TiddlerWithParamsPlugin = {
 major: 1, minor: 0, revision: 1, 
 date: new Date(2006,3,22), 
 type: 'plugin',
 source: "http://tiddlywiki.abego-software.de/#TiddlerWithParamsPlugin"
};

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

var indexInArray = function(array, item) {
 for (var i = 0; i < array.length; i++) {
 if (array[i] == item) {
 return i;
 }
 }
 return -1;
}

var myEscapeRegExp = function(s)
{
 // The original escapeRegExp function does not work with Safari (2.0.3) 
 // since the $& is not implemented.
var t = s.replace(/\\/g, "\\\\");
 t = t.replace(/\^/g, "\\^");
 t = t.replace(/\$/g, "\\$");
 t = t.replace(/\*/g, "\\*");
 t = t.replace(/\+/g, "\\+");
 t = t.replace(/\?/g, "\\?");
 t = t.replace(/\(/g, "\\(");
 t = t.replace(/\)/g, "\\)");
 t = t.replace(/\=/g, "\\=");
 t = t.replace(/\!/g, "\\!");
 t = t.replace(/\|/g, "\\|");
 t = t.replace(/\,/g, "\\,");
 t = t.replace(/\{/g, "\\{");
 t = t.replace(/\}/g, "\\}");
 t = t.replace(/\[/g, "\\[");
 t = t.replace(/\]/g, "\\]");
 t = t.replace(/\./g, "\\.");

 return t;
}

// ---------------------------------------------------------------------------
// The (hijacked) tiddler Macro Handler 
// ---------------------------------------------------------------------------

config.macros.tiddler.handler = function(place,macroName,params,wikifier,paramString,tiddler) {
 
 var className = null;
 var argsStart = -1;
 var doWikify = true;

 var iParams = 1;
 if (params[iParams] != "asText" && params[iParams] != "with:") {
 className = params[iParams++];
 }
 if (params[iParams] == "asText") {
 iParams++;
 doWikify = false;
 }
 if (params[iParams] == "with:") {
 iParams++;
 argsStart = iParams;
 }
 
 var wrapper = createTiddlyElement(place,"span",null,className ? className : null,null);
 var text = store.getTiddlerText(params[0]);
 if(text) {
 // Check for recursion
 var tiddlerName = params[0];
 var stack = config.macros.tiddler.tiddlerStack;
 if (stack.find(tiddlerName) !== null) return;

 if (argsStart >= 0) {
 // The params between the "with:" and the "prefix:" (or the end) are the arguments,
 // The param behind the "prefix:" is the prefix before the placeholder numbers.
 var argsEnd = params.length;
 var prefix = "$";
 var prefixIndex = indexInArray(params, "prefix:");
 if (prefixIndex >= argsStart) {
 argsEnd = prefixIndex;
 if (prefixIndex < (params.length-1)) {
 prefix = params[prefixIndex+1];
 }
 }
 // to avoid any "special RE chars" problems with the prefix string escape all chars.
 prefix = myEscapeRegExp(prefix);
 
 var args = params.slice(argsStart, argsEnd);
 var n = Math.min(args.length, 9);
 for (var i = 0; i < n; i++) {
 var value = args[i];
 
 var placeholderRE = new RegExp(prefix+(i+1),"mg");
 text = text.replace(placeholderRE, value);
 }
 }
 stack.push(tiddlerName);
 try {
 if (doWikify) {
 wikify(text,wrapper,null,store.getTiddler(params[0]));
 } else {
 wrapper.appendChild(document.createTextNode(text));
 }
 } finally { 
 stack.pop();
 }
 }
}
config.macros.tiddler.tiddlerStack = [];

// End of "install only once"
}

//============================================================================
// End of TiddlerWithParamsPlugin
//============================================================================
//}}}
/***
!Licence and Copyright
Copyright (c) abego Software ~GmbH, 2006 ([[www.abego-software.de|http://www.abego-software.de]])

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.

Neither the name of abego Software nor the names of its contributors may be
used to endorse or promote products derived from this software without specific
prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
***/
Here are some tips for best performance:
* [[Hang on to your objects]] - use K2 objects effectively
* [[Take a breath]] - do not flood ~AppService with calls
* [[Get multiple properties]] - do not make many single property calls
* [[Call CloseResults after MediaMgr Enumerate* calls]] - do not run out database selectors
* [[GetXmlResults performance]] - performance issues with ~GetXmlResults
* [[Writing a clip cache]] - how to write a clip cache to keep track of a bin's contents
* [[Don't hardcode the V: drive]] - get the volume name programatically instead
If you are having trouble with your system please [[click here and follow these steps|Troubleshooting and gathering useful logs]] for gathering and sending useful information.
The following are properties of a transfer which can be retrieved calling the ~TransferQueue's ~GetXfrItemProperty() function

|!Property|!Value|
|state|0 = pending, 1 = transferring, 2 = completed, 3 = error, 4 = aborted |
|percentage|transfer percentage complete|
|averagerate|average transfer rate|
|currentrate|current transfer rate|
|bytestransferred|bytes transferred so far|
|fieldstransferred|fields transferred so far|
|totalbytes|total bytes|
|totalfields|total fields|
|errorcode|error code if transfer fails - see "state" above and [[Grass Valley K2 error codes]]|

See [[Transfer a File]] for an example of how to query the properties of a transfer.
Below is an example for adding a request to the ~TransferQueue to transfer a clip between two K2s. The example also shows how to query properties of the transfer item. 

See [[Transfer clips: importing and exporting various file types]] for more ~AddXfrItem examples of transferring clips and importing & exporting GXF and MXF files.

See [[Transfer Item properties]] for a list of all transfer item properties that may be queried.

{{{
[C#]
// create a transfer queue object
IXfrQueue ixfrQueue = iappServer.CreateXfrQueue(appName);

// transfer a clip from between two K2s; get a token back from the
// TransferQueue so that we can query properties of the transfer
int token;
ixfrQueue.AddXfrItem(
"K2-1/explodedFile/V:/default/Clip",
"K2-2/explodedFile/V:/default/Clip",
"Send to Stream",
out token); // token for querying properties

// query for a property
object data;
ixfrQueue.GetXfrItemProperty(token, "percentage", out data);
Console.WriteLine("percentage = " + data);

// get xml data about the transfer item
string xmlData;
ixfrQueue.GetXfrItemXml(token, out xmlData);
Console.WriteLine("result = " + xmlData);
}}}
{{{
[C++]
// create a transfer queue object
IXfrQueuePtr spXfrQueue;
HRESULT hr = spAppServer->CreateXfrQueue(m_bstrAppName, &spXfrQueue);

// transfer a clip from between two K2s; get a token back from the
// TransferQueue so that we can query properties of the transfer
long token;
spXfrQueue->AddXfrItem(
 _bstr_t("K2-1/explodedFile/V:/default/Clip"),
 _bstr_t("K2-2/explodedFile/V:/default/Clip"),
 _bstr_t("Send to Stream"),
 &token); // token for querying properties

// query for a property
VARIANT data;
spXfrQueue->GetXfrItemProperty(token, _bstr_t("Percentage"), &data);
long nPercentage = data.lVal;
printf("percentage = %d\n", nPercentage);

// get xml data about the transfer item
BSTR xmlData;
spXfrQueue->GetXfrItemXml(token, &xmlData);

CString sXmlData = xmlData;
printf("xmlData = %s\n\n", sXmlData);
}}}
Below are examples of how to call ~AddXfrItem to transfer clips and import or export GXF and MXF files. For a more thorough example on how to create the objects necessary for transferring files see [[Transfer a File]].

To transfer a clip between K2s and/or Profiles:
{{{
int token;
ixfrQueue.AddXfrItem(
 "K2-1/explodedFile/V:/default/Clip", "K2-2/explodedFile/V:/default/Clip", 
 "Send to Stream", out token);
}}}
To export a clip to a GXF file (C:\temp\Clip.gxf)):
{{{
int token;
ixfrQueue.AddXfrItem(
 "K2-1/explodedFile/V:/default/Clip", "K2-1/gxfFile/C:/temp/Clip.gxf", 
 "Send to File", out token);
}}}
To import a GXF file (C:\temp\Clip.gxf):
{{{
int token;
ixfrQueue.AddXfrItem(
 "K2-1/gxfFile/C:/temp/Clip.gxf", "K2-1/explodedFile/V:/default/Clip", 
 "Import File", out token);
}}}
To export a clip to an MXF file (C:\temp\Clip.mxf):
{{{
int token;
ixfrQueue.AddXfrItem(
 "K2-1/explodedFile/V:/default/Clip", "K2-1/mxfFile/C:/temp/Clip.mxf",
 "Send to File", out token);
}}}
To import an MXF file (C:\temp\Clip.mxf):
{{{
int token;
ixfrQueue.AddXfrItem(
 "K2-1/mxfFile/C:/temp/Clip.mxf", "K2-1/explodedFile/V:/default/Clip", 
 "Import File", out token);
}}}
To export a clip to a MOV file (C:\temp\Clip.mov):
{{{
int token;
ixfrQueue.AddXfrItem(
 "K2-1/explodedFile/V:/default/Clip", "K2-1/movFile/C:/temp/Clip.mov",
 "Send to File", out token);
}}}
To import a MOV file (C:\temp\Clip.mov):
{{{
int token;
ixfrQueue.AddXfrItem(
 "K2-1/movFile/C:/temp/Clip.mov", "K2-1/explodedFile/V:/default/Clip", 
 "Import File", out token);
}}}
To export a clip to an AVI file (C:\temp\Clip.avi):
{{{
int token;
ixfrQueue.AddXfrItem(
 "K2-1/explodedFile/V:/default/Clip", "K2-1/aviFile/C:/temp/Clip.avi",
 "Send to File", out token);
}}}
To import an AVI file (C:\temp\Clip.avi):
{{{
int token;
ixfrQueue.AddXfrItem(
 "K2-1/aviFile/C:/temp/Clip.avi", "K2-1/explodedFile/V:/default/Clip", 
 "Import File", out token);
}}}
To import an MPEG file (C:\temp\Clip.mpg):
@@color(red):Note: MPEG file must be top field first (TFF) material. We do not support bottom field first (BFF) material@@
{{{
int token;
ixfrQueue.AddXfrItem(
 "K2-1/mpgFile/C:/temp/Clip.mpg", "K2-1/explodedFile/V:/default/Clip", 
 "Import File", out token);
}}}
To import a WAV file (C:\temp\Sound.wav) - NOTE: WAV file must be 48 ~KHz, 16bit, mono or stereo:
{{{
int token;
ixfrQueue.AddXfrItem(
 "K2-1/wavFile/C:/temp/Sound.wav", "K2-1/explodedFile/V:/default/Sound", 
 "Import File", out token);
}}}
''When troubleshooting problems, try to gather as much of the following information as possible. Brief or incomplete descriptions only result in many back-and-forth emails to gather more information and frustration on both ends. Providing the following information helps us respond to and resolve problems more quickly.''

!!0. The Short Answer:
''Here is the minimum information that we __always__ want to quickly help fix problems:''

''Log File'': We always want mlog.mmf files (see step 4 below). Zip them up before you send or FTP them.
''Good Description'': Explain what you are trying to do and what error you are seeing (see step 2 below).
''When'': Tell us the approximate date & time when the problem happens (see step 2 below). This helps us know where to look in the logs.

If nothing else, please send this information!  Often we get screenshots and an incomplete description. Without a log, a good description, and a date & time, we are often only aware of symptoms or the result of the problem.

!!1. Check for common issues first:
''Make sure your drives are not full:''
__C: drive:__
Open Explorer, right-click on the C:\ drive, and select properties. 

[img[C: Drive Space|images/CDriveSpace.png]]

Make sure that you have plenty of free space (i.e. the pink part of the pie chart). If not, look for large files or directories that you can delete. If you have exported many GXF or MXF files to your hard drive these often use a lot of space.

__V: drive:__
- In ~AppCenter look in your Recycle Bin. Is it full? Delete these clips to free up drive space. Remember that if you delete a clip in ~AppCenter it will be moved to the Recycle Bin. If you hold the shift key down when you delete, the clip will be permanently deleted instead.

- In ~AppCenter in the ~MediaMonitor window click on the “Look In:” drop down menu. 
- Click on “Organize Bins” menu item
- Click on the “V:” drive letter
- On the left it will show used space, free space, and total space. See how much is free. 

[img[V: Drive Space|images/VDriveSpace.png]]

If the V: drive is still full consider exporting and deleting clips.

''High CPU common issues:''
__3rd party applications__:
High CPU activity in Windows can result in delays in sending commands down to the real-time processor. This can happen when unsupported software has been installed that competes with K2 Media Client applications. Virus scanners and screen savers can cause this type of problem, too, since they can start automatically and consume system resources. Double-check all software that is installed on the K2 Client to ensure that it does not conflict. 

__Scheduled tasks__:
From Control Panel run "Scheduled Tasks". Make sure that no tasks are scheduled. Some users schedule maintenance tasks (e.g. disk defragmenting) on their work and personal PCs. Make sure that no tasks are scheduled for your K2 Client.

__Turn off automatic updates__:
From Control Panel run "Automatic Updates". Set the radio button to one of these options:
- Turn off Automatic Updates
- Notify me but don't automatically download or install them.

Setting Automatic Updates to one of these options will prevent Windows from automatically updating your K2 Client. You should contact your Grass Valley service representative to determine which Windows updates you should load on your K2 Client.

!!2. Document the problem:
Write a good explanation of the problem. A description that is too brief (like "deletes fail") does not help anyone. Try to document the date and time of the problem and clip or bin names involved, if applicable. Knowing what to look for and when to look (date and time) greatly helps us find and solve the problem.

Note any important setup conditions or observations for the problem: does the problem happen only after startup? only after running for 10 days? only after performing a particular sequence of steps?
!!3. Describe your system:
''Get system version number:''
For K2 Clients in ~AppCenter, click on the menu Help / About ~AppCenter. 

[img[AppCenter About Box|images/AboutBox.png]]

Write down the System Version number. It will be a number that looks something like "3.2.58.871".

For K2 Servers, since ~AppCenter does not run on a K2 Server, you need to manually check a file to see the version of software that is installed. Log.exe is a good file to check. So in Explorer go to the C:\profile directory. Right-click on log.exe. Click on the Version tab and write down the File Version number. It will be a number that looks like "3.2.58.871".

[img[Log Version|images/LogVersion.png]]

''@@color(red):Note:@@'' make sure that your software versions match between your K2 Clients and K2 Servers!

''Get other system information:''
Write down: 
- the number of K2 Clients and K2 Servers you have
- are these SD or HD systems?
- NTSC or PAL?
- how many player and recorder channels?
- include a system diagram if you have one
!!4. Get K2 logs:
''K2 Clients:''
On a K2 Client in ~AppCenter select the menu "System / Export" Log. This will create a zip file containing:
- the system log: c:\logs\mlog.mmf
- the configuration file c:\profile\config\config.xml
- and a file containing software version info: version.txt ([[click here to see the contents of this file|version file in exported log zip file]])

''K2 Servers:''
On a K2 Server you need to manually zip up the file c:\logs\mlog.mmf. This is because ~AppService does not run on a K2 Server so a mechanism for exporting and zipping the file is not present.

The mlog.mmf file is always 146 MB. By zipping it you can reduce this to approximately 5 MB.

You can download a log reader for viewing the mlog.mmf by [[clicking here|zip/K2LogReader.zip]].

Additionally, you can download ~LogInjector to add your own messages to the K2 log. Sometimes this is useful for adding details about operations or tests you are performing. [[Click here to download LogInjector|zip/LogInjector.zip]].

For more information about how to use the log reader please see the K2 Logging video in the [[Videos]] section.
!!5. Get Event Viewer logs:
Click Start button / Run and type "eventvwr". Click OK.

We're interested in the Application and System logs. To save these:
- right click on "Application" or "System"
- select "Save Log File As..."
- change Save as type to "Text (Tab delimited)(*.txt)" and type in a meaningful name such as "~K2Client07_ApplicationLog.txt". 
- click the Save button to save it.
!!6. ~TaskMgr checks you can also run:
Click Start button / Run and type "taskmgr". Click OK.

''Setup'': select Processes tab. Select View menu and "Select Columns..." Add checkboxes to "PID", "CPU Usage", "Memory Usage", "Handle Count" and "Thread Count". Press OK.

''High CPU check'': select Processes tab. Is the CPU Usage bar graph high (> 70%)? If so, select Processes tab. Click on the "CPU" column header to sort from highest to lowest CPU usage. Scroll to the top. Press Shift & Print Screen buttons together to take a screenshot of this then paste this image into a Word document or into Paint, label it, and save it.

''High memory usage check'': in the Processes tab click on the "Mem Usage" column header to sort from highest to lowest memory usage. Scroll to the top. Press Shift & Print Screen buttons together to take a screenshot of this then paste this image into a Word document or into Paint, label it, and save it.

''High handle count check'': in the Processes tab click on the "Handles" column header to sort from highest to lowest handle usage. Scroll to the top. Press Shift & Print Screen buttons together to take a screenshot of this then paste this image into a Word document or into Paint, label it, and save it.

''High thread count check'': in the Processes tab click on the "Threads" column header to sort from highest to lowest thread usage. Scroll to the top.Press Shift & Print Screen buttons together to take a screenshot of this then paste this image into a Word document or into Paint, label it, and save it.
!!7. Upload your files and request help:
Create a zip file of all of your files and logs. Give it a meaningful name (possibly containing customer name, date, problem, etc).

If the zip file is greater than 3 MB upload them to ftp://ftp.thomsongrassvalley.com/incoming. Make a separate subdirectory there for your zip file. You can also upload them to your own FTP server and provide us with details on how to retrieve it.

If the zip file is less than 3MB you can attach it to your email message.

Email us at ''[[GVDeveloperSupport@thomson.net|mailto:GVDeveloperSupport@thomson.net?subject=Error logs]]''. Explain your problem to us, tell us what you have uploaded, and what help you are looking for.
Also see the [[Videos]] section for video tutorials.

* [[The Role of AppService]] - how the ~AppService gives access to a K2's functionality
* [[Client call sequence]] - how a client connects to a K2 to do work
* ''[[Playlists, sections, and events.|Playlists sections and events]]'' - information about how playlists work
* [[Additional information]] - suites, security, channel stealing
The following videos contain both video and audio. So make sure that you have sound enabled on your system.

----
''~AppServer API:''
This video shows how the ~AppServer API works on the K2 and how the K2 allocates resources for client applications.

[[Click here for Flash|videos/AppServerAPI/AppServerAPI.html]] or [[Click here for Windows Media Video|videos/AppServerAPI/AppServerAPI.wmv]] 
Length: 6 minutes, WMV file size: 10 MB

----
''C# Scripts:'' 
This video shows how to write simple C# scripts for controlling the K2 using the ~AppServer API.

[[Click here for Flash|videos/CSharpScripts/CSharpScripts.html]] or [[Click here for Windows Media Video|videos/CSharpScripts/CSharpScripts.wmv]] 
Length: 27 minutes, WMV file size: 39 MB

----
''K2 Logging:''
This video explains about how the K2 logs information as well as how to use the log reading tools.

[[Click here for Flash|videos/K2Logging/K2Logging.html]] or [[Click here for Windows Media Video|videos/K2Logging/K2Logging.wmv]] 
Length: 32 minutes, WMV file size: 53 MB

<div class='toolbar' macro='toolbar -closeTiddler closeOthers +editTiddler permalink references jump'></div>
<div class='title' macro='view title'></div>
<!-- <div class='subtitle'><span macro='view modifier link'></span>, <span macro='view modified date [[DD MMM YYYY]]'></span> (created <span macro='view created date [[DD MMM YYYY]]'></span>)</div> 
<div class='tagging' macro='tagging'></div> -->
<div class='tagged' macro='tags'></div>
<div class='viewer' macro='view text wikified'></div>
<div class='tagClear'></div>
Please visit our forum where you can suggest, vote and comment on ideas for improving our K2 Servers or the K2 Developer's Guide:<script>document.write("\r\n\u003Ciframe src=\"http://gvdevsupport.uservoice.com/pages/general/widgets/top3.html\"\r\n  name=\"uservoice_top3\" id=\"uservoice_top3\"\r\n  width=\"100%\" height=\"230px\"\r\n  frameborder=\"0\" scrolling=\"no\"\r\n  allowtransparency=\"true\"\u003E\r\n\u003C/iframe\u003E\r\n")
var widget = document.getElementById('uservoice_top3');
var width = widget.clientWidth;
var height = width > 300 ? 190 : (-0.733 * width) + 400
widget.height = height + 'px';
widget.width = 600;</script>
22 Apr 2009 - added information about [[Setting Aspect Ratio Conversion]]
11 Mar 2009 - added [[Summit Emulator]]
03 Mar 2009 - added Summit information about [[Live Play]], [[Flying Mix Effects]], and [[Playlist Mix Effects]]
06 Jan 2009 - added information about [[Context changes]]
17 Dec 2009 - updated [[K2 Emulator]] to version 3.2.74.1000
09 Nov 2008 - updated [[Sample Projects]]
06 Nov 2008 - added [[K2 Summit information and API additions]].
27 Oct 2008 - added how to [[merge, replace or add ancillary data to a clip|Merging, replacing, or adding ancillary data to a clip]].
05 Aug 2008 - added how to [[Import and delete tracks from a clip]].
13 Jun 2008 - added [[K2 SNMP MIBs]] information

[[Click here|History]] to see the past history of changes.
Many people need to keep track of the contents of a bin. The best way to do this is to:
# Get a list of all assets in a bin using ~MediaMgr's [[EnumerateAssets and GetResultProperty|Get a list of clips]] calls. 
# Cache the results you want in your data structure.
# Periodically call ~MediaMgr's [[GetXmlChanges|Getting event notifications]] function to see what has changed and update the results in your data structure.

The reason for using this technique is two-fold:
# ~EnumerateAssets is an expensive database calls on the K2. You want to call this as infrequently as possible.
# ~GetXmlChanges is a light-weight call. It only returns the information that has changed since the last time you called it. 

By taking advantage of this and updating your data structure, you'll get the best performance and eliminate unnecessary calls.
/***
|''Name:''|XHTML10Plugin|
|''Version:''|1.0.1 (2006-09-16)|
|''Source:''|http://tiddlywiki.abego-software.de/#XHTML10Plugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]|
|''Copyright:''|&copy; 2005-2006 [[abego Software|http://www.abego-software.de]]|
|''~CoreVersion:''|2.1.0|
|''Browser:''|Firefox 1.5.0.2 or better; Internet Explorer 6.0|

Make your ~TiddlyWiki XHTML 1.0 compliant format.

Once the plugin is installed the existing tiddlers of that TiddlyWiki are automatically converted to the new (XHTML 1.0 compliant) format on the first save. After that all changes are stored in the XHTML format.

!Source Code
***/
//{{{
// Ensure the Plugin is only installed once.
//
if (!version.extensions.XHTML10Plugin) {

if (version.major < 2 || (version.major == 2 && version.minor < 1)) {
 (function() {
 var s = "Use TiddlyWiki 2.1 or better to run the XHTML10Plugin.";
 alert(s);
 throw s;
 })();
}

version.extensions.XHTML10Plugin = {
 major: 1, minor: 0, revision: 1,
 date: new Date(2006, 8, 16),
 source: "http://tiddlywiki.abego-software.de/#XHTML10Plugin",
 licence: "[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]",
 copyright: "Copyright (c) abego Software GmbH, 2005-2006 (www.abego-software.de)",
};

// Ensure the global abego namespace is set up.
if (!window.abego) window.abego = {};


//--------------------------------
// XHTML10Saver (inherits from SaverBase)

abego.XHTML10Saver = function() {};

abego.XHTML10Saver.prototype = new SaverBase();

abego.XHTML10Saver.prototype.externalizeTiddler = function(store, tiddler) {
 try {
 var s = '';
 store.forEachField(tiddler, 
 function(tiddler, fieldName, value) {
 // don't store stuff from the temp namespace
 if (!fieldName.match(/^temp\./)) {
 if (value)
 value = value.htmlEncode();
 s += ['<pre title="',fieldName,'">',value,'</pre>'].join("");
 }
 });
 return ['<div title="',tiddler.title.htmlEncode(),'">',s,'</div>'].join("");

 } catch (e) {
 showException(e, config.messages.tiddlerSaveError.format([tiddler.title]));
 return '';
 }
};

abego.XHTML10Saver.prototype.externalize = function(store) {
 return ["<div class='twXHTML10'>\n",SaverBase.prototype.externalize.apply(this, arguments),"\n</div>"].join("");
};


//--------------------------------
// Overwrite TiddlyWiki.prototype.getSaver to use the XHTML10 format on save

TiddlyWiki.prototype.getSaver = function() {
 if (!this.saver) 
 this.saver = new abego.XHTML10Saver();
 return this.saver;
};

//======================================
// Install the Loader into the HTML page

(function() {
 // The loader code will be inserted into the PostHead markup block,
 // so it can be executed before tiddlers are loaded. We cannot just put this
 // code into a normal plugin since this "load" code is required to load
 // tiddlers. I.e. this code must be executed before any tiddlers/plugins
 // can be loaded.

 var getXHTML10LoaderBlock = function() {
 // The loader code in a big JavaScript string.
 // You may get a non-stringified version of the XHTML10Loader source code at
 // http://tiddlywiki.abego-software.de/archive/XHTML10Plugin/XHTML10Loader.1.0.1.js

 XHTML10LoaderCode = 
 "if (!window.abego) window.abego = {};\nif (!abego.XHTML10Loader) {\n\t//-"+
 "-------------------------------\n\t// abego.XHTML10Loader (inherits from"+
 " LoaderBase)\n\t\n\tabego.XHTML10Loader = function() {};\n\tabego.XHTML10Loa"+
 "der.prototype = new LoaderBase();\n\t\n\tabego.XHTML10Loader.prototype.lin"+
 "go = {\n\t\tunnamedValue: \"Unnamed value\",\n\t\tredefining: \"Redefining valu"+
 "e of %0\",\n\t\tnoXHTML10Format: \"Storage not in XHTML 1.0 format\"\n\t}\n\t\n\ta"+
 "bego.XHTML10Loader.prototype.getTitle = function(store, e) {\n\t\tvar tit"+
 "le = null;\n\t\tif(e.getAttribute)\n\t\t\ttitle = e.getAttribute('title');\n\t\t"+
 "if(!title && e.id) {\t\n\t\t\tvar lenPrefix = store.idPrefix.length;\n\t\t\tif "+
 "(e.id.substr(0,lenPrefix) == store.idPrefix)\n\t\t\t\ttitle = e.id.substr(l"+
 "enPrefix);\n\t\t}\n\t\treturn title;\n\t};\n\t\n\tabego.XHTML10Loader.prototype.in"+
 "ternalizeTiddler = function(store, tiddler, title, data) {\n\t\tvar field"+
 "s = {};\n\t\tvar elems = data.childNodes;\n\t\tfor(var i = 0; i < elems.leng"+
 "th; i++) {\n\t\t\tvar e = elems[i];\n\t\t\tvar name = e.getAttribute('title');"+
 "\n\t\t\tif (!name) \n\t\t\t\tthrow this.lingo.unnamedValue;\n\t\t\tif (fields[name]"+
 " !== undefined) \n\t\t\t\tthrow this.lingo.redefining.format([name]);\n\t\t\tfi"+
 "elds[name] = getNodeText(e.firstChild); \n\t\t}\n\t\n\t\t// Extract (and remov"+
 "e) the standard fields from the extended fields\n\t\tvar text = fields.te"+
 "xt;\n\t\tvar modifier = fields.modifier;\n\t\tvar modified = Date.convertFro"+
 "mYYYYMMDDHHMM(fields.modified);\n\t\tvar c = fields.created;\n\t\tvar create"+
 "d = c ? Date.convertFromYYYYMMDDHHMM(c) : modified;\n\t\tvar tags = field"+
 "s.tags;\n\t\tdelete fields.modifier;\n\t\tdelete fields.modified;\n\t\tdelete f"+
 "ields.created;\n\t\tdelete fields.tags;\n\t\tdelete fields.text;\n\t\tdelete fi"+
 "elds.title;\n\t\n\t\ttiddler.assign(title,text,modifier,modified,tags,creat"+
 "ed,fields);\n\t\t\n\t\treturn tiddler;\n\t};\n\t\n\tvar findRootNode = function(no"+
 "des) {\n\t\tif (nodes) {\n\t\t\t// skip leading text nodes\n\t\t\tfor (var i = 0;"+
 " i < nodes.length; i++)\n\t\t\t\tif (nodes[i].nodeType != 3)\n\t\t\t\t\tbreak;\n\t\t"+
 "\t\t\t\n\t\t\tif (i < nodes.length && nodes[i].className == 'twXHTML10')\n\t\t\t\t"+
 "return nodes[i];\n\t\t}\n\t};\n\t\n\tabego.XHTML10Loader.prototype.loadTiddlers"+
 " = function(store,nodes) {\n\t\t// in the twXHMTL10 format all tiddler el"+
 "ements are contained in one enclosing DIV\n\t\t// that contains the forma"+
 "t information\n\t\tvar root = findRootNode(nodes)\n\t\tif (!root) \n\t\t\tthrow "+
 "this.lingo.noXHTML10Format;\n\t\treturn LoaderBase.prototype.loadTiddlers"+
 ".apply(this, [store, root.childNodes]);\n\t};\n\t\n\t\n\t//-------------------"+
 "-------------\n\t// Hijack the loadFromDiv\n\t(function() {\n\t\tvar origTidd"+
 "lyWikiLoadFromDiv = TiddlyWiki.prototype.loadFromDiv;\n\t\tTiddlyWiki.pro"+
 "totype.loadFromDiv = function(srcID,idPrefix) {\n\t\t\t// use the XHTML 1."+
 "0 loader when the storearea is in 'twXHTML10' format,\n\t\t\t// otherwise "+
 "use the default loader\n\t\t\tvar e = document.getElementById(srcID);\n\t\t\ti"+
 "f (e && findRootNode(e.childNodes))\n\t\t\t\tthis.loader = new abego.XHTML1"+
 "0Loader();\n\t\t\treturn origTiddlyWikiLoadFromDiv.apply(this, arguments);"+
 "\n\t\t};\n\t})();\n}\n\n";
 return '<'+'script type="text/javascript">\n//<![CDATA[\n'+XHTML10LoaderCode+'\n//]]>\n</script'+'>\n';
 };

 var insertLoaderBlock = function() {
 if (!store)
 throw "XHTML10LoaderInstaller must run as a plugin";
 
 var START = "<!--XHMTL10Loader-START-->";
 var END = "<!--XHMTL10Loader-END-->";
 
 var postHeadText = store.getTiddlerText("MarkupPostHead");
 if (postHeadText.getChunk(START, END)) 
 return; // already installed

 postHeadText += "\n"+START+getXHTML10LoaderBlock()+END+"\n";
 var tiddler = store.getTiddler("MarkupPostHead");
 var tags = tiddler ? tiddler.tags : [];
 store.saveTiddler("MarkupPostHead","MarkupPostHead",postHeadText,config.options.txtUserName,new Date(),tags);
 alert("XHTML10Loader installed.\nPlease save and reload your TiddlyWiki to complete the installation. After that your TiddlyWiki will be stored in an XHTML 1.0 compliant format.");
 };
 
 insertLoaderBlock(); 
})();

} // of single install

//}}}
/***
|''Name:''|YourSearchPlugin|
|''Version:''|2.1.3 (2008-04-16)|
|''Source:''|http://tiddlywiki.abego-software.de/#YourSearchPlugin|
|''Author:''|UdoBorkowski (ub [at] abego-software [dot] de)|
|''Licence:''|[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]|
|''Copyright:''|&copy; 2005-2008 [[abego Software|http://www.abego-software.de]]|
|''~CoreVersion:''|2.1.0|
|''Community:''|[[del.icio.us|http://del.icio.us/post?url=http://tiddlywiki.abego-software.de/index.html%23YourSearchPlugin]]|
|''Browser:''|Firefox 1.0.4+; Firefox 1.5; ~InternetExplorer 6.0|
!About YourSearch
YourSearch gives you a bunch of new features to simplify and speed up your daily searches in TiddlyWiki. It seamlessly integrates into the standard TiddlyWiki search: just start typing into the 'search' field and explore!

For more information see [[Help|YourSearch Help]].
!Compatibility
This plugin requires TiddlyWiki 2.1. 
Check the [[archive|http://tiddlywiki.abego-software.de/archive]] for ~YourSearchPlugins supporting older versions of TiddlyWiki.
!Source Code
***/
/***
This plugin's source code is compressed (and hidden). Use this [[link|http://tiddlywiki.abego-software.de/archive/YourSearchPlugin/Plugin-YourSearch-src.2.1.3.js]] to get the readable source code.
***/
///%
if(!version.extensions.YourSearchPlugin){version.extensions.YourSearchPlugin={major:2,minor:1,revision:3,source:"http://tiddlywiki.abego-software.de/#YourSearchPlugin",licence:"[[BSD open source license (abego Software)|http://www.abego-software.de/legal/apl-v10.html]]",copyright:"Copyright (c) abego Software GmbH, 2005-2008 (www.abego-software.de)"};if(!window.abego){window.abego={};}
if(!Array.forEach){Array.forEach=function(_1,_2,_3){for(var i=0,len=_1.length;i<len;i++){_2.call(_3,_1[i],i,_1);}};Array.prototype.forEach=function(_5,_6){for(var i=0,len=this.length;i<len;i++){_5.call(_6,this[i],i,this);}};}
abego.toInt=function(s,_9){if(!s){return _9;}
var n=parseInt(s);return(n==NaN)?_9:n;};abego.createEllipsis=function(_b){var e=createTiddlyElement(_b,"span");e.innerHTML="&hellip;";};abego.shallowCopy=function(_d){if(!_d){return _d;}
var _e={};for(var n in _d){_e[n]=_d[n];}
return _e;};abego.copyOptions=function(_10){return!_10?{}:abego.shallowCopy(_10);};abego.countStrings=function(_11,s){if(!s){return 0;}
var len=s.length;var n=0;var _15=0;while(1){var i=_11.indexOf(s,_15);if(i<0){return n;}
n++;_15=i+len;}
return n;};abego.getBracedText=function(_17,_18,_19){if(!_18){_18=0;}
var re=/\{([^\}]*)\}/gm;re.lastIndex=_18;var m=re.exec(_17);if(m){var s=m[1];var _1d=abego.countStrings(s,"{");if(!_1d){if(_19){_19.lastIndex=re.lastIndex;}
return s;}
var len=_17.length;for(var i=re.lastIndex;i<len&&_1d;i++){var c=_17.charAt(i);if(c=="{"){_1d++;}else{if(c=="}"){_1d--;}}}
if(!_1d){if(_19){_19.lastIndex=i-1;}
return _17.substring(m.index+1,i-1);}}};abego.select=function(_21,_22,_23,_24){if(!_24){_24=[];}
_21.forEach(function(t){if(_22.call(_23,t)){_24.push(t);}});return _24;};abego.consumeEvent=function(e){if(e.stopPropagation){e.stopPropagation();}
if(e.preventDefault){e.preventDefault();}
e.cancelBubble=true;e.returnValue=true;};abego.TiddlerFilterTerm=function(_27,_28){if(!_28){_28={};}
var _29=_27;if(!_28.textIsRegExp){_29=_27.escapeRegExp();if(_28.fullWordMatch){_29="\\b"+_29+"\\b";}}
var _2a=new RegExp(_29,"m"+(_28.caseSensitive?"":"i"));this.tester=new abego.MultiFieldRegExpTester(_2a,_28.fields,_28.withExtendedFields);};abego.TiddlerFilterTerm.prototype.test=function(_2b){return this.tester.test(_2b);};abego.parseNewTiddlerCommandLine=function(s){var m=/(.*?)\.(?:\s+|$)([^#]*)(#.*)?/.exec(s);if(!m){m=/([^#]*)()(#.*)?/.exec(s);}
if(m){var r;if(m[3]){var s2=m[3].replace(/#/g,"");r=s2.parseParams("tag");}else{r=[[]];}
var _30=m[2]?m[2].trim():"";r.push({name:"text",value:_30});r[0].text=[_30];return{title:m[1].trim(),params:r};}else{return{title:s.trim(),params:[[]]};}};abego.parseTiddlerFilterTerm=function(_31,_32,_33){var re=/\s*(?:(?:\{([^\}]*)\})|(?:(=)|([#%!])|(?:(\w+)\s*\:(?!\/\/))|(?:(?:("(?:(?:\\")|[^"])+")|(?:\/((?:(?:\\\/)|[^\/])+)\/)|(\w+\:\/\/[^\s]+)|([^\s\)\-\"]+)))))/mg;var _35={"!":"title","%":"text","#":"tags"};var _36={};var _37;re.lastIndex=_32;while(1){var i=re.lastIndex;var m=re.exec(_31);if(!m||m.index!=i){throw"Word or String literal expected";}
if(m[1]){var _3a={};var _3b=abego.getBracedText(_31,0,_3a);if(!_3b){throw"Invalid {...} syntax";}
var f=Function("tiddler","return ("+_3b+");");return{func:f,lastIndex:_3a.lastIndex,markRE:null};}
if(m[2]){_37=true;}else{if(m[3]){_36[_35[m[3]]]=1;}else{if(m[4]){_36[m[4]]=1;}else{var _3d=m[6];var _3e=m[5]?window.eval(m[5]):m[6]?m[6]:m[7]?m[7]:m[8];var _33=abego.copyOptions(_33);_33.fullWordMatch=_37;_33.textIsRegExp=_3d;var _3f=[];for(var n in _36){_3f.push(n);}
if(_3f.length==0){_33.fields=_33.defaultFields;}else{_33.fields=_3f;_33.withExtendedFields=false;}
var _41=new abego.TiddlerFilterTerm(_3e,_33);var _42=_3d?_3e:_3e.escapeRegExp();if(_42&&_37){_42="\\b"+_42+"\\b";}
return{func:function(_43){return _41.test(_43);},lastIndex:re.lastIndex,markRE:_42?"(?:"+_42+")":null};}}}}};abego.BoolExp=function(s,_45,_46){this.s=s;var _47=_46&&_46.defaultOperationIs_OR;var _48=/\s*(?:(\-|not)|(\())/gi;var _49=/\s*\)/g;var _4a=/\s*(?:(and|\&\&)|(or|\|\|))/gi;var _4b=/\s*[^\)\s]/g;var _4c=/\s*(\-|not)?(\s*\()?/gi;var _4d;var _4e=function(_4f){_4c.lastIndex=_4f;var m=_4c.exec(s);var _51;var _52;if(m&&m.index==_4f){_4f+=m[0].length;_51=m[1];if(m[2]){var e=_4d(_4f);_49.lastIndex=e.lastIndex;if(!_49.exec(s)){throw"Missing ')'";}
_52={func:e.func,lastIndex:_49.lastIndex,markRE:e.markRE};}}
if(!_52){_52=_45(s,_4f,_46);}
if(_51){_52.func=(function(f){return function(_55){return!f(_55);};})(_52.func);_52.markRE=null;}
return _52;};_4d=function(_56){var _57=_4e(_56);while(1){var l=_57.lastIndex;_4a.lastIndex=l;var m=_4a.exec(s);var _5a;var _5b;if(m&&m.index==l){_5a=!m[1];_5b=_4e(_4a.lastIndex);}else{try{_5b=_4e(l);}
catch(e){return _57;}
_5a=_47;}
_57.func=(function(_5c,_5d,_5e){return _5e?function(_5f){return _5c(_5f)||_5d(_5f);}:function(_60){return _5c(_60)&&_5d(_60);};})(_57.func,_5b.func,_5a);_57.lastIndex=_5b.lastIndex;if(!_57.markRE){_57.markRE=_5b.markRE;}else{if(_5b.markRE){_57.markRE=_57.markRE+"|"+_5b.markRE;}}}};var _61=_4d(0);this.evalFunc=_61.func;if(_61.markRE){this.markRegExp=new RegExp(_61.markRE,_46.caseSensitive?"mg":"img");}};abego.BoolExp.prototype.exec=function(){return this.evalFunc.apply(this,arguments);};abego.BoolExp.prototype.getMarkRegExp=function(){return this.markRegExp;};abego.BoolExp.prototype.toString=function(){return this.s;};abego.MultiFieldRegExpTester=function(re,_63,_64){this.re=re;this.fields=_63?_63:["title","text","tags"];this.withExtendedFields=_64;};abego.MultiFieldRegExpTester.prototype.test=function(_65){var re=this.re;for(var i=0;i<this.fields.length;i++){var s=store.getValue(_65,this.fields[i]);if(typeof s=="string"&&re.test(s)){return this.fields[i];}}
if(this.withExtendedFields){return store.forEachField(_65,function(_69,_6a,_6b){return typeof _6b=="string"&&re.test(_6b)?_6a:null;},true);}
return null;};abego.TiddlerQuery=function(_6c,_6d,_6e,_6f,_70){if(_6e){this.regExp=new RegExp(_6c,_6d?"mg":"img");this.tester=new abego.MultiFieldRegExpTester(this.regExp,_6f,_70);}else{this.expr=new abego.BoolExp(_6c,abego.parseTiddlerFilterTerm,{defaultFields:_6f,caseSensitive:_6d,withExtendedFields:_70});}
this.getQueryText=function(){return _6c;};this.getUseRegExp=function(){return _6e;};this.getCaseSensitive=function(){return _6d;};this.getDefaultFields=function(){return _6f;};this.getWithExtendedFields=function(){return _70;};};abego.TiddlerQuery.prototype.test=function(_71){if(!_71){return false;}
if(this.regExp){return this.tester.test(_71);}
return this.expr.exec(_71);};abego.TiddlerQuery.prototype.filter=function(_72){return abego.select(_72,this.test,this);};abego.TiddlerQuery.prototype.getMarkRegExp=function(){if(this.regExp){return"".search(this.regExp)>=0?null:this.regExp;}
return this.expr.getMarkRegExp();};abego.TiddlerQuery.prototype.toString=function(){return(this.regExp?this.regExp:this.expr).toString();};abego.PageWiseRenderer=function(){this.firstIndexOnPage=0;};merge(abego.PageWiseRenderer.prototype,{setItems:function(_73){this.items=_73;this.setFirstIndexOnPage(0);},getMaxPagesInNavigation:function(){return 10;},getItemsCount:function(_74){return this.items?this.items.length:0;},getCurrentPageIndex:function(){return Math.floor(this.firstIndexOnPage/this.getItemsPerPage());},getLastPageIndex:function(){return Math.floor((this.getItemsCount()-1)/this.getItemsPerPage());},setFirstIndexOnPage:function(_75){this.firstIndexOnPage=Math.min(Math.max(0,_75),this.getItemsCount()-1);},getFirstIndexOnPage:function(){this.firstIndexOnPage=Math.floor(this.firstIndexOnPage/this.getItemsPerPage())*this.getItemsPerPage();return this.firstIndexOnPage;},getLastIndexOnPage:function(){return Math.min(this.getFirstIndexOnPage()+this.getItemsPerPage()-1,this.getItemsCount()-1);},onPageChanged:function(_76,_77){},renderPage:function(_78){if(_78.beginRendering){_78.beginRendering(this);}
try{if(this.getItemsCount()){var _79=this.getLastIndexOnPage();var _7a=-1;for(var i=this.getFirstIndexOnPage();i<=_79;i++){_7a++;_78.render(this,this.items[i],i,_7a);}}}
finally{if(_78.endRendering){_78.endRendering(this);}}},addPageNavigation:function(_7c){if(!this.getItemsCount()){return;}
var _7d=this;var _7e=function(e){if(!e){var e=window.event;}
abego.consumeEvent(e);var _80=abego.toInt(this.getAttribute("page"),0);var _81=_7d.getCurrentPageIndex();if(_80==_81){return;}
var _82=_80*_7d.getItemsPerPage();_7d.setFirstIndexOnPage(_82);_7d.onPageChanged(_80,_81);};var _83;var _84=this.getCurrentPageIndex();var _85=this.getLastPageIndex();if(_84>0){_83=createTiddlyButton(_7c,"Previous","Go to previous page (Shortcut: Alt-'<')",_7e,"prev");_83.setAttribute("page",(_84-1).toString());_83.setAttribute("accessKey","<");}
for(var i=-this.getMaxPagesInNavigation();i<this.getMaxPagesInNavigation();i++){var _87=_84+i;if(_87<0){continue;}
if(_87>_85){break;}
var _88=(i+_84+1).toString();var _89=_87==_84?"currentPage":"otherPage";_83=createTiddlyButton(_7c,_88,"Go to page %0".format([_88]),_7e,_89);_83.setAttribute("page",(_87).toString());}
if(_84<_85){_83=createTiddlyButton(_7c,"Next","Go to next page (Shortcut: Alt-'>')",_7e,"next");_83.setAttribute("page",(_84+1).toString());_83.setAttribute("accessKey",">");}}});abego.LimitedTextRenderer=function(){var _8a=40;var _8b=4;var _8c=function(_8d,_8e,_8f){var n=_8d.length;if(n==0){_8d.push({start:_8e,end:_8f});return;}
var i=0;for(;i<n;i++){var _92=_8d[i];if(_92.start<=_8f&&_8e<=_92.end){var r;var _94=i+1;for(;_94<n;_94++){r=_8d[_94];if(r.start>_8f||_8e>_92.end){break;}}
var _95=_8e;var _96=_8f;for(var j=i;j<_94;j++){r=_8d[j];_95=Math.min(_95,r.start);_96=Math.max(_96,r.end);}
_8d.splice(i,_94-i,{start:_95,end:_96});return;}
if(_92.start>_8f){break;}}
_8d.splice(i,0,{start:_8e,end:_8f});};var _98=function(_99){var _9a=0;for(var i=0;i<_99.length;i++){var _9c=_99[i];_9a+=_9c.end-_9c.start;}
return _9a;};var _9d=function(c){return(c>="a"&&c<="z")||(c>="A"&&c<="Z")||c=="_";};var _9f=function(s,_a1){if(!_9d(s[_a1])){return null;}
for(var i=_a1-1;i>=0&&_9d(s[i]);i--){}
var _a3=i+1;var n=s.length;for(i=_a1+1;i<n&&_9d(s[i]);i++){}
return{start:_a3,end:i};};var _a5=function(s,_a7,_a8){var _a9;if(_a8){_a9=_9f(s,_a7);}else{if(_a7<=0){return _a7;}
_a9=_9f(s,_a7-1);}
if(!_a9){return _a7;}
if(_a8){if(_a9.start>=_a7-_8b){return _a9.start;}
if(_a9.end<=_a7+_8b){return _a9.end;}}else{if(_a9.end<=_a7+_8b){return _a9.end;}
if(_a9.start>=_a7-_8b){return _a9.start;}}
return _a7;};var _aa=function(s,_ac){var _ad=[];if(_ac){var _ae=0;var n=s.length;var _b0=0;do{_ac.lastIndex=_ae;var _b1=_ac.exec(s);if(_b1){if(_ae<_b1.index){var t=s.substring(_ae,_b1.index);_ad.push({text:t});}
_ad.push({text:_b1[0],isMatch:true});_ae=_b1.index+_b1[0].length;}else{_ad.push({text:s.substr(_ae)});break;}}while(true);}else{_ad.push({text:s});}
return _ad;};var _b3=function(_b4){var _b5=0;for(var i=0;i<_b4.length;i++){if(_b4[i].isMatch){_b5++;}}
return _b5;};var _b7=function(s,_b9,_ba,_bb,_bc){var _bd=Math.max(Math.floor(_bc/(_bb+1)),_8a);var _be=Math.max(_bd-(_ba-_b9),0);var _bf=Math.min(Math.floor(_ba+_be/3),s.length);var _c0=Math.max(_bf-_bd,0);_c0=_a5(s,_c0,true);_bf=_a5(s,_bf,false);return{start:_c0,end:_bf};};var _c1=function(_c2,s,_c4){var _c5=[];var _c6=_b3(_c2);var pos=0;for(var i=0;i<_c2.length;i++){var t=_c2[i];var _ca=t.text;if(t.isMatch){var _cb=_b7(s,pos,pos+_ca.length,_c6,_c4);_8c(_c5,_cb.start,_cb.end);}
pos+=_ca.length;}
return _c5;};var _cc=function(s,_ce,_cf){var _d0=_cf-_98(_ce);while(_d0>0){if(_ce.length==0){_8c(_ce,0,_a5(s,_cf,false));return;}else{var _d1=_ce[0];var _d2;var _d3;if(_d1.start==0){_d2=_d1.end;if(_ce.length>1){_d3=_ce[1].start;}else{_8c(_ce,_d2,_a5(s,_d2+_d0,false));return;}}else{_d2=0;_d3=_d1.start;}
var _d4=Math.min(_d3,_d2+_d0);_8c(_ce,_d2,_d4);_d0-=(_d4-_d2);}}};var _d5=function(_d6,s,_d8,_d9,_da){if(_d9.length==0){return;}
var _db=function(_dc,s,_de,_df,_e0){var t;var _e2;var pos=0;var i=0;var _e5=0;for(;i<_de.length;i++){t=_de[i];_e2=t.text;if(_df<pos+_e2.length){_e5=_df-pos;break;}
pos+=_e2.length;}
var _e6=_e0-_df;for(;i<_de.length&&_e6>0;i++){t=_de[i];_e2=t.text.substr(_e5);_e5=0;if(_e2.length>_e6){_e2=_e2.substr(0,_e6);}
if(t.isMatch){createTiddlyElement(_dc,"span",null,"marked",_e2);}else{createTiddlyText(_dc,_e2);}
_e6-=_e2.length;}
if(_e0<s.length){abego.createEllipsis(_dc);}};if(_d9[0].start>0){abego.createEllipsis(_d6);}
var _e7=_da;for(var i=0;i<_d9.length&&_e7>0;i++){var _e9=_d9[i];var len=Math.min(_e9.end-_e9.start,_e7);_db(_d6,s,_d8,_e9.start,_e9.start+len);_e7-=len;}};this.render=function(_eb,s,_ed,_ee){if(s.length<_ed){_ed=s.length;}
var _ef=_aa(s,_ee);var _f0=_c1(_ef,s,_ed);_cc(s,_f0,_ed);_d5(_eb,s,_ef,_f0,_ed);};};(function(){function alertAndThrow(msg){alert(msg);throw msg;}
if(version.major<2||(version.major==2&&version.minor<1)){alertAndThrow("YourSearchPlugin requires TiddlyWiki 2.1 or newer.\n\nCheck the archive for YourSearch plugins\nsupporting older versions of TiddlyWiki.\n\nArchive: http://tiddlywiki.abego-software.de/archive");}
abego.YourSearch={};var _f2;var _f3;var _f4=function(_f5){_f2=_f5;};var _f6=function(){return _f2?_f2:[];};var _f7=function(){return _f2?_f2.length:0;};var _f8=4;var _f9=10;var _fa=2;var _fb=function(s,re){var m=s.match(re);return m?m.length:0;};var _ff=function(_100,_101){var _102=_101.getMarkRegExp();if(!_102){return 1;}
var _103=_100.title.match(_102);var _104=_103?_103.length:0;var _105=_fb(_100.getTags(),_102);var _106=_103?_103.join("").length:0;var _107=_100.title.length>0?_106/_100.title.length:0;var rank=_104*_f8+_105*_fa+_107*_f9+1;return rank;};var _109=function(_10a,_10b,_10c,_10d,_10e,_10f){_f3=null;var _110=_10a.reverseLookup("tags",_10f,false);try{var _111=[];if(config.options.chkSearchInTitle){_111.push("title");}
if(config.options.chkSearchInText){_111.push("text");}
if(config.options.chkSearchInTags){_111.push("tags");}
_f3=new abego.TiddlerQuery(_10b,_10c,_10d,_111,config.options.chkSearchExtendedFields);}
catch(e){return[];}
var _112=_f3.filter(_110);var _113=abego.YourSearch.getRankFunction();for(var i=0;i<_112.length;i++){var _115=_112[i];var rank=_113(_115,_f3);_115.searchRank=rank;}
if(!_10e){_10e="title";}
var _117=function(a,b){var _11a=a.searchRank-b.searchRank;if(_11a==0){if(a[_10e]==b[_10e]){return(0);}else{return(a[_10e]<b[_10e])?-1:+1;}}else{return(_11a>0)?-1:+1;}};_112.sort(_117);return _112;};var _11b=80;var _11c=50;var _11d=250;var _11e=50;var _11f=25;var _120=10;var _121="yourSearchResult";var _122="yourSearchResultItems";var _123;var _124;var _125;var _126;var _127;var _128=function(){if(version.extensions.YourSearchPlugin.styleSheetInited){return;}
version.extensions.YourSearchPlugin.styleSheetInited=true;setStylesheet(store.getTiddlerText("YourSearchStyleSheet"),"yourSearch");};var _129=function(){return _124!=null&&_124.parentNode==document.body;};var _12a=function(){if(_129()){document.body.removeChild(_124);}};var _12b=function(e){_12a();var _12d=this.getAttribute("tiddlyLink");if(_12d){var _12e=this.getAttribute("withHilite");var _12f=highlightHack;if(_12e&&_12e=="true"&&_f3){highlightHack=_f3.getMarkRegExp();}
story.displayTiddler(this,_12d);highlightHack=_12f;}
return(false);};var _130=function(){if(!_125){return;}
var root=_125;var _132=findPosX(root);var _133=findPosY(root);var _134=root.offsetHeight;var _135=_132;var _136=_133+_134;var _137=findWindowWidth();if(_137<_124.offsetWidth){_124.style.width=(_137-100)+"px";_137=findWindowWidth();}
var _138=_124.offsetWidth;if(_135+_138>_137){_135=_137-_138-30;}
if(_135<0){_135=0;}
_124.style.left=_135+"px";_124.style.top=_136+"px";_124.style.display="block";};var _139=function(){if(_124){window.scrollTo(0,ensureVisible(_124));}
if(_125){window.scrollTo(0,ensureVisible(_125));}};var _13a=function(){_130();_139();};var _13b;var _13c;var _13d=new abego.PageWiseRenderer();var _13e=function(_13f){this.itemHtml=store.getTiddlerText("YourSearchItemTemplate");if(!this.itemHtml){alertAndThrow("YourSearchItemTemplate not found");}
this.place=document.getElementById(_122);if(!this.place){this.place=createTiddlyElement(_13f,"div",_122);}};merge(_13e.prototype,{render:function(_140,_141,_142,_143){_13b=_143;_13c=_141;var item=createTiddlyElement(this.place,"div",null,"yourSearchItem");item.innerHTML=this.itemHtml;applyHtmlMacros(item,null);refreshElements(item,null);},endRendering:function(_145){_13c=null;}});var _146=function(){if(!_124||!_125){return;}
var html=store.getTiddlerText("YourSearchResultTemplate");if(!html){html="<b>Tiddler YourSearchResultTemplate not found</b>";}
_124.innerHTML=html;applyHtmlMacros(_124,null);refreshElements(_124,null);var _148=new _13e(_124);_13d.renderPage(_148);_13a();};_13d.getItemsPerPage=function(){var n=(config.options.chkPreviewText)?abego.toInt(config.options.txtItemsPerPageWithPreview,_120):abego.toInt(config.options.txtItemsPerPage,_11f);return(n>0)?n:1;};_13d.onPageChanged=function(){_146();};var _14a=function(){if(_125==null||!config.options.chkUseYourSearch){return;}
if((_125.value==_123)&&_123&&!_129()){if(_124&&(_124.parentNode!=document.body)){document.body.appendChild(_124);_13a();}else{abego.YourSearch.onShowResult(true);}}};var _14b=function(){_12a();_124=null;_123=null;};var _14c=function(self,e){while(e!=null){if(self==e){return true;}
e=e.parentNode;}
return false;};var _14f=function(e){if(e.target==_125){return;}
if(e.target==_126){return;}
if(_124&&_14c(_124,e.target)){return;}
_12a();};var _151=function(e){if(e.keyCode==27){_12a();}};addEvent(document,"click",_14f);addEvent(document,"keyup",_151);var _153=function(text,_155,_156){_123=text;_f4(_109(store,text,_155,_156,"title","excludeSearch"));abego.YourSearch.onShowResult();};var _157=function(_158,_159,_15a,_15b,_15c,_15d){_128();_123="";var _15e=null;var _15f=function(txt){if(config.options.chkUseYourSearch){_153(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch);}else{story.search(txt.value,config.options.chkCaseSensitiveSearch,config.options.chkRegExpSearch);}
_123=txt.value;};var _161=function(e){_15f(_125);return false;};var _163=function(e){if(!e){var e=window.event;}
_125=this;switch(e.keyCode){case 13:if(e.ctrlKey&&_127&&_129()){_127.onclick.apply(_127,[e]);}else{_15f(this);}
break;case 27:if(_129()){_12a();}else{this.value="";clearMessage();}
break;}
if(String.fromCharCode(e.keyCode)==this.accessKey||e.altKey){_14a();}
if(this.value.length<3&&_15e){clearTimeout(_15e);}
if(this.value.length>2){if(this.value!=_123){if(!config.options.chkUseYourSearch||config.options.chkSearchAsYouType){if(_15e){clearTimeout(_15e);}
var txt=this;_15e=setTimeout(function(){_15f(txt);},500);}}else{if(_15e){clearTimeout(_15e);}}}
if(this.value.length==0){_12a();}};var _166=function(e){this.select();clearMessage();_14a();};var args=_15c.parseParams("list",null,true);var _169=getFlag(args,"buttonAtRight");var _16a=getParam(args,"sizeTextbox",this.sizeTextbox);var btn;if(!_169){btn=createTiddlyButton(_158,this.label,this.prompt,_161);}
var txt=createTiddlyElement(_158,"input",null,null,null);if(_15a[0]){txt.value=_15a[0];}
txt.onkeyup=_163;txt.onfocus=_166;txt.setAttribute("size",_16a);txt.setAttribute("accessKey",this.accessKey);txt.setAttribute("autocomplete","off");if(config.browser.isSafari){txt.setAttribute("type","search");txt.setAttribute("results","5");}else{txt.setAttribute("type","text");}
if(_169){btn=createTiddlyButton(_158,this.label,this.prompt,_161);}
_125=txt;_126=btn;};var _16d=function(){_12a();var _16e=_f6();var n=_16e.length;if(n){var _170=[];for(var i=0;i<n;i++){_170.push(_16e[i].title);}
story.displayTiddlers(null,_170);}};var _172=function(_173,_174,_175,_176){invokeMacro(_173,"option",_174,_175,_176);var elem=_173.lastChild;var _178=elem.onclick;elem.onclick=function(e){var _17a=_178.apply(this,arguments);_146();return _17a;};return elem;};var _17b=function(s){var _17d=["''","{{{","}}}","//","<<<","/***","***/"];var _17e="";for(var i=0;i<_17d.length;i++){if(i!=0){_17e+="|";}
_17e+="("+_17d[i].escapeRegExp()+")";}
return s.replace(new RegExp(_17e,"mg"),"").trim();};var _180=function(){var i=_13b;return(i>=0&&i<=9)?(i<9?(i+1):0):-1;};var _182=new abego.LimitedTextRenderer();var _183=function(_184,s,_186){_182.render(_184,s,_186,_f3.getMarkRegExp());};var _187=TiddlyWiki.prototype.saveTiddler;TiddlyWiki.prototype.saveTiddler=function(_188,_189,_18a,_18b,_18c,tags,_18e){_187.apply(this,arguments);_14b();};var _18f=TiddlyWiki.prototype.removeTiddler;TiddlyWiki.prototype.removeTiddler=function(_190){_18f.apply(this,arguments);_14b();};config.macros.yourSearch={label:"yourSearch",prompt:"Gives access to the current/last YourSearch result",handler:function(_191,_192,_193,_194,_195,_196){if(_193.length==0){return;}
var name=_193[0];var func=config.macros.yourSearch.funcs[name];if(func){func(_191,_192,_193,_194,_195,_196);}},tests:{"true":function(){return true;},"false":function(){return false;},"found":function(){return _f7()>0;},"previewText":function(){return config.options.chkPreviewText;}},funcs:{itemRange:function(_199){if(_f7()){var _19a=_13d.getLastIndexOnPage();var s="%0 - %1".format([_13d.getFirstIndexOnPage()+1,_19a+1]);createTiddlyText(_199,s);}},count:function(_19c){createTiddlyText(_19c,_f7().toString());},query:function(_19d){if(_f3){createTiddlyText(_19d,_f3.toString());}},version:function(_19e){var t="YourSearch %0.%1.%2".format([version.extensions.YourSearchPlugin.major,version.extensions.YourSearchPlugin.minor,version.extensions.YourSearchPlugin.revision]);var e=createTiddlyElement(_19e,"a");e.setAttribute("href","http://tiddlywiki.abego-software.de/#YourSearchPlugin");e.innerHTML="<font color=\"black\" face=\"Arial, Helvetica, sans-serif\">"+t+"<font>";},copyright:function(_1a1){var e=createTiddlyElement(_1a1,"a");e.setAttribute("href","http://www.abego-software.de");e.innerHTML="<font color=\"black\" face=\"Arial, Helvetica, sans-serif\">&copy; 2005-2008 <b><font color=\"red\">abego</font></b> Software<font>";},newTiddlerButton:function(_1a3){if(_f3){var r=abego.parseNewTiddlerCommandLine(_f3.getQueryText());var btn=config.macros.newTiddler.createNewTiddlerButton(_1a3,r.title,r.params,"new tiddler","Create a new tiddler based on search text. (Shortcut: Ctrl-Enter; Separators: '.', '#')",null,"text");var _1a6=btn.onclick;btn.onclick=function(){_12a();_1a6.apply(this,arguments);};_127=btn;}},linkButton:function(_1a7,_1a8,_1a9,_1aa,_1ab,_1ac){if(_1a9<2){return;}
var _1ad=_1a9[1];var text=_1a9<3?_1ad:_1a9[2];var _1af=_1a9<4?text:_1a9[3];var _1b0=_1a9<5?null:_1a9[4];var btn=createTiddlyButton(_1a7,text,_1af,_12b,null,null,_1b0);btn.setAttribute("tiddlyLink",_1ad);},closeButton:function(_1b2,_1b3,_1b4,_1b5,_1b6,_1b7){var _1b8=createTiddlyButton(_1b2,"close","Close the Search Results (Shortcut: ESC)",_12a);},openAllButton:function(_1b9,_1ba,_1bb,_1bc,_1bd,_1be){var n=_f7();if(n==0){return;}
var _1c0=n==1?"open tiddler":"open all %0 tiddlers".format([n]);var _1c1=createTiddlyButton(_1b9,_1c0,"Open all found tiddlers (Shortcut: Alt-O)",_16d);_1c1.setAttribute("accessKey","O");},naviBar:function(_1c2,_1c3,_1c4,_1c5,_1c6,_1c7){_13d.addPageNavigation(_1c2);},"if":function(_1c8,_1c9,_1ca,_1cb,_1cc,_1cd){if(_1ca.length<2){return;}
var _1ce=_1ca[1];var _1cf=(_1ce=="not");if(_1cf){if(_1ca.length<3){return;}
_1ce=_1ca[2];}
var test=config.macros.yourSearch.tests[_1ce];var _1d1=false;try{if(test){_1d1=test(_1c8,_1c9,_1ca,_1cb,_1cc,_1cd)!=_1cf;}else{_1d1=(!eval(_1ce))==_1cf;}}
catch(ex){}
if(!_1d1){_1c8.style.display="none";}},chkPreviewText:function(_1d2,_1d3,_1d4,_1d5,_1d6,_1d7){var _1d8=_1d4.slice(1).join(" ");var elem=_172(_1d2,"chkPreviewText",_1d5,_1d7);elem.setAttribute("accessKey","P");elem.title="Show text preview of found tiddlers (Shortcut: Alt-P)";return elem;}}};config.macros.foundTiddler={label:"foundTiddler",prompt:"Provides information on the tiddler currently processed on the YourSearch result page",handler:function(_1da,_1db,_1dc,_1dd,_1de,_1df){var name=_1dc[0];var func=config.macros.foundTiddler.funcs[name];if(func){func(_1da,_1db,_1dc,_1dd,_1de,_1df);}},funcs:{title:function(_1e2,_1e3,_1e4,_1e5,_1e6,_1e7){if(!_13c){return;}
var _1e8=_180();var _1e9=_1e8>=0?"Open tiddler (Shortcut: Alt-%0)".format([_1e8.toString()]):"Open tiddler";var btn=createTiddlyButton(_1e2,null,_1e9,_12b,null);btn.setAttribute("tiddlyLink",_13c.title);btn.setAttribute("withHilite","true");_183(btn,_13c.title,_11b);if(_1e8>=0){btn.setAttribute("accessKey",_1e8.toString());}},tags:function(_1eb,_1ec,_1ed,_1ee,_1ef,_1f0){if(!_13c){return;}
_183(_1eb,_13c.getTags(),_11c);},text:function(_1f1,_1f2,_1f3,_1f4,_1f5,_1f6){if(!_13c){return;}
_183(_1f1,_17b(_13c.text),_11d);},field:function(_1f7,_1f8,_1f9,_1fa,_1fb,_1fc){if(!_13c){return;}
var name=_1f9[1];var len=_1f9.length>2?abego.toInt(_1f9[2],_11e):_11e;var v=store.getValue(_13c,name);if(v){_183(_1f7,_17b(v),len);}},number:function(_200,_201,_202,_203,_204,_205){var _206=_180();if(_206>=0){var text="%0)".format([_206.toString()]);createTiddlyElement(_200,"span",null,"shortcutNumber",text);}}}};var opts={chkUseYourSearch:true,chkPreviewText:true,chkSearchAsYouType:true,chkSearchInTitle:true,chkSearchInText:true,chkSearchInTags:true,chkSearchExtendedFields:true,txtItemsPerPage:_11f,txtItemsPerPageWithPreview:_120};for(var n in opts){if(config.options[n]==undefined){config.options[n]=opts[n];}}
config.shadowTiddlers.AdvancedOptions+="\n<<option chkUseYourSearch>> Use 'Your Search' //([[more options|YourSearch Options]]) ([[help|YourSearch Help]])// ";config.shadowTiddlers["YourSearch Help"]="!Field Search\nWith the Field Search you can restrict your search to certain fields of a tiddler, e.g"+" only search the tags or only the titles. The general form is //fieldname//'':''//textToSearch// (e."+"g. {{{title:intro}}}). In addition one-character shortcuts are also supported for the standard field"+"s {{{title}}}, {{{text}}} and {{{tags}}}:\n|!What you want|!What you type|!Example|\n|Search ''titles "+"only''|start word with ''!''|{{{!jonny}}} (shortcut for {{{title:jonny}}})|\n|Search ''contents/text "+"only''|start word with ''%''|{{{%football}}} (shortcut for {{{text:football}}})|\n|Search ''tags only"+"''|start word with ''#''|{{{#Plugin}}} (shortcut for {{{tags:Plugin}}})|\n\nUsing this feature you may"+" also search the extended fields (\"Metadata\") introduced with TiddlyWiki 2.1, e.g. use {{{priority:1"+"}}} to find all tiddlers with the priority field set to \"1\".\n\nYou may search a word in more than one"+" field. E.g. {{{!#Plugin}}} (or {{{title:tags:Plugin}}} in the \"long form\") finds tiddlers containin"+"g \"Plugin\" either in the title or in the tags (but does not look for \"Plugin\" in the text). \n\n!Boole"+"an Search\nThe Boolean Search is useful when searching for multiple words.\n|!What you want|!What you "+"type|!Example|\n|''All words'' must exist|List of words|{{{jonny jeremy}}} (or {{{jonny and jeremy}}}"+")|\n|''At least one word'' must exist|Separate words by ''or''|{{{jonny or jeremy}}}|\n|A word ''must "+"not exist''|Start word with ''-''|{{{-jonny}}} (or {{{not jonny}}})|\n\n''Note:'' When you specify two"+" words, separated with a space, YourSearch finds all tiddlers that contain both words, but not neces"+"sarily next to each other. If you want to find a sequence of word, e.g. '{{{John Brown}}}', you need"+" to put the words into quotes. I.e. you type: {{{\"john brown\"}}}.\n\nUsing parenthesis you may change "+"the default \"left to right\" evaluation of the boolean search. E.g. {{{not (jonny or jeremy)}}} finds"+" all tiddlers that contain neither \"jonny\" nor \"jeremy. In contrast to this {{{not jonny or jeremy}}"+"} (i.e. without parenthesis) finds all tiddlers that either don't contain \"jonny\" or that contain \"j"+"eremy\".\n\n!'Exact Word' Search\nBy default a search result all matches that 'contain' the searched tex"+"t. E.g. if you search for {{{Task}}} you will get all tiddlers containing 'Task', but also '~Complet"+"edTask', '~TaskForce' etc.\n\nIf you only want to get the tiddlers that contain 'exactly the word' you"+" need to prefix it with a '='. E.g. typing '=Task' will find the tiddlers that contain the word 'Tas"+"k', ignoring words that just contain 'Task' as a substring.\n\n!~CaseSensitiveSearch and ~RegExpSearch"+"\nThe standard search options ~CaseSensitiveSearch and ~RegExpSearch are fully supported by YourSearc"+"h. However when ''~RegExpSearch'' is on Filtered and Boolean Search are disabled.\n\nIn addition you m"+"ay do a \"regular expression\" search even with the ''~RegExpSearch'' set to false by directly enterin"+"g the regular expression into the search field, framed with {{{/.../}}}. \n\nExample: {{{/m[ae][iy]er/"+"}}} will find all tiddlers that contain either \"maier\", \"mayer\", \"meier\" or \"meyer\".\n\n!~JavaScript E"+"xpression Filtering\nIf you are familiar with JavaScript programming and know some TiddlyWiki interna"+"ls you may also use JavaScript expression for the search. Just enter a JavaScript boolean expression"+" into the search field, framed with {{{ { ... } }}}. In the code refer to the variable tiddler and e"+"valuate to {{{true}}} when the given tiddler should be included in the result. \n\nExample: {{{ { tidd"+"ler.modified > new Date(\"Jul 4, 2005\")} }}} returns all tiddler modified after July 4th, 2005.\n\n!Com"+"bined Search\nYou are free to combine the various search options. \n\n''Examples''\n|!What you type|!Res"+"ult|\n|{{{!jonny !jeremy -%football}}}|all tiddlers with both {{{jonny}}} and {{{jeremy}}} in its tit"+"les, but no {{{football}}} in content.|\n|{{{#=Task}}}|All tiddlers tagged with 'Task' (the exact wor"+"d). Tags named '~CompletedTask', '~TaskForce' etc. are not considered.|\n\n!Access Keys\nYou are encour"+"aged to use the access keys (also called \"shortcut\" keys) for the most frequently used operations. F"+"or quick reference these shortcuts are also mentioned in the tooltip for the various buttons etc.\n\n|"+"!Key|!Operation|\n|{{{Alt-F}}}|''The most important keystroke'': It moves the cursor to the search in"+"put field so you can directly start typing your query. Pressing {{{Alt-F}}} will also display the pr"+"evious search result. This way you can quickly display multiple tiddlers using \"Press {{{Alt-F}}}. S"+"elect tiddler.\" sequences.|\n|{{{ESC}}}|Closes the [[YourSearch Result]]. When the [[YourSearch Resul"+"t]] is already closed and the cursor is in the search input field the field's content is cleared so "+"you start a new query.|\n|{{{Alt-1}}}, {{{Alt-2}}},... |Pressing these keys opens the first, second e"+"tc. tiddler from the result list.|\n|{{{Alt-O}}}|Opens all found tiddlers.|\n|{{{Alt-P}}}|Toggles the "+"'Preview Text' mode.|\n|{{{Alt-'<'}}}, {{{Alt-'>'}}}|Displays the previous or next page in the [[Your"+"Search Result]].|\n|{{{Return}}}|When you have turned off the 'as you type' search mode pressing the "+"{{{Return}}} key actually starts the search (as does pressing the 'search' button).|\n\n//If some of t"+"hese shortcuts don't work for you check your browser if you have other extensions installed that alr"+"eady \"use\" these shortcuts.//";config.shadowTiddlers["YourSearch Options"]="|>|!YourSearch Options|\n|>|<<option chkUseYourSearch>> Use 'Your Search'|\n|!|<<option chkPreviewText"+">> Show Text Preview|\n|!|<<option chkSearchAsYouType>> 'Search As You Type' Mode (No RETURN required"+" to start search)|\n|!|Default Search Filter:<<option chkSearchInTitle>>Title ('!')     <<option chk"+"SearchInText>>Text ('%')     <<option chkSearchInTags>>Tags ('#')    <<option chkSearchExtendedFiel"+"ds>>Extended Fields<html><br><font size=\"-2\">The fields of a tiddlers that are searched when you don"+"'t explicitly specify a filter in the search text <br>(Explictly specify fields using one or more '!"+"', '%', '#' or 'fieldname:' prefix before the word/text to find).</font></html>|\n|!|Number of items "+"on search result page: <<option txtItemsPerPage>>|\n|!|Number of items on search result page with pre"+"view text: <<option txtItemsPerPageWithPreview>>|\n";config.shadowTiddlers["YourSearchStyleSheet"]="/***\n!~YourSearchResult Stylesheet\n***/\n/*{{{*/\n.yourSearchResult {\n\tposition: absolute;\n\twidth: 800"+"px;\n\n\tpadding: 0.2em;\n\tlist-style: none;\n\tmargin: 0;\n\n\tbackground: #ffd;\n\tborder: 1px solid DarkGra"+"y;\n}\n\n/*}}}*/\n/***\n!!Summary Section\n***/\n/*{{{*/\n.yourSearchResult .summary {\n\tborder-bottom-width:"+" thin;\n\tborder-bottom-style: solid;\n\tborder-bottom-color: #999999;\n\tpadding-bottom: 4px;\n}\n\n.yourSea"+"rchRange, .yourSearchCount, .yourSearchQuery   {\n\tfont-weight: bold;\n}\n\n.yourSearchResult .summary ."+"button {\n\tfont-size: 10px;\n\n\tpadding-left: 0.3em;\n\tpadding-right: 0.3em;\n}\n\n.yourSearchResult .summa"+"ry .chkBoxLabel {\n\tfont-size: 10px;\n\n\tpadding-right: 0.3em;\n}\n\n/*}}}*/\n/***\n!!Items Area\n***/\n/*{{{*"+"/\n.yourSearchResult .marked {\n\tbackground: none;\n\tfont-weight: bold;\n}\n\n.yourSearchItem {\n\tmargin-to"+"p: 2px;\n}\n\n.yourSearchNumber {\n\tcolor: #808080;\n}\n\n\n.yourSearchTags {\n\tcolor: #008000;\n}\n\n.yourSearc"+"hText {\n\tcolor: #808080;\n\tmargin-bottom: 6px;\n}\n\n/*}}}*/\n/***\n!!Footer\n***/\n/*{{{*/\n.yourSearchFoote"+"r {\n\tmargin-top: 8px;\n\tborder-top-width: thin;\n\tborder-top-style: solid;\n\tborder-top-color: #999999;"+"\n}\n\n.yourSearchFooter a:hover{\n\tbackground: none;\n\tcolor: none;\n}\n/*}}}*/\n/***\n!!Navigation Bar\n***/"+"\n/*{{{*/\n.yourSearchNaviBar a {\n\tfont-size: 16px;\n\tmargin-left: 4px;\n\tmargin-right: 4px;\n\tcolor: bla"+"ck;\n\ttext-decoration: underline;\n}\n\n.yourSearchNaviBar a:hover {\n\tbackground-color: none;\n}\n\n.yourSe"+"archNaviBar .prev {\n\tfont-weight: bold;\n\tcolor: blue;\n}\n\n.yourSearchNaviBar .currentPage {\n\tcolor: #"+"FF0000;\n\tfont-weight: bold;\n\ttext-decoration: none;\n}\n\n.yourSearchNaviBar .next {\n\tfont-weight: bold"+";\n\tcolor: blue;\n}\n/*}}}*/\n";config.shadowTiddlers["YourSearchResultTemplate"]="<!--\n{{{\n-->\n<span macro=\"yourSearch if found\">\n<!-- The Summary Header ============================"+"================ -->\n<table class=\"summary\" border=\"0\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\">"+"<tbody>\n  <tr>\n\t<td align=\"left\">\n\t\tYourSearch Result <span class=\"yourSearchRange\" macro=\"yourSearc"+"h itemRange\"></span>\n\t\t&nbsp;of&nbsp;<span class=\"yourSearchCount\" macro=\"yourSearch count\"></span>\n"+"\t\tfor&nbsp;<span class=\"yourSearchQuery\" macro=\"yourSearch query\"></span>\n\t</td>\n\t<td class=\"yourSea"+"rchButtons\" align=\"right\">\n\t\t<span macro=\"yourSearch chkPreviewText\"></span><span class=\"chkBoxLabel"+"\">preview text</span>\n\t\t<span macro=\"yourSearch newTiddlerButton\"></span>\n\t\t<span macro=\"yourSearch openAllButton\"></span>\n\t\t<span macro=\"yourSearch lin"+"kButton 'YourSearch Options' options 'Configure YourSearch'\"></span>\n\t\t<span macro=\"yourSearch linkB"+"utton 'YourSearch Help' help 'Get help how to use YourSearch'\"></span>\n\t\t<span macro=\"yourSearch clo"+"seButton\"></span>\n\t</td>\n  </tr>\n</tbody></table>\n\n<!-- The List of Found Tiddlers ================="+"=========================== -->\n<div id=\"yourSearchResultItems\" itemsPerPage=\"25\" itemsPerPageWithPr"+"eview=\"10\"></div>\n\n<!-- The Footer (with the Navigation) ==========================================="+"= -->\n<table class=\"yourSearchFooter\" border=\"0\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\"><tbody"+">\n  <tr>\n\t<td align=\"left\">\n\t\tResult page: <span class=\"yourSearchNaviBar\" macro=\"yourSearch naviBar"+"\"></span>\n\t</td>\n\t<td align=\"right\"><span macro=\"yourSearch version\"></span>, <span macro=\"yourSearc"+"h copyright\"></span>\n\t</td>\n  </tr>\n</tbody></table>\n<!-- end of the 'tiddlers found' case ========="+"================================== -->\n</span>\n\n\n<!-- The \"No tiddlers found\" case ================="+"========================== -->\n<span macro=\"yourSearch if not found\">\n<table class=\"summary\" border="+"\"0\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\"><tbody>\n  <tr>\n\t<td align=\"left\">\n\t\tYourSearch Resu"+"lt: No tiddlers found for <span class=\"yourSearchQuery\" macro=\"yourSearch query\"></span>.\n\t</td>\n\t<t"+"d class=\"yourSearchButtons\" align=\"right\">\n\t\t<span macro=\"yourSearch newTiddlerButton\"></span>\n\t\t<span macro=\"yourSearch linkButton 'YourSearch Options'"+" options 'Configure YourSearch'\"></span>\n\t\t<span macro=\"yourSearch linkButton 'YourSearch Help' help"+" 'Get help how to use YourSearch'\"></span>\n\t\t<span macro=\"yourSearch closeButton\"></span>\n\t</td>\n  <"+"/tr>\n</tbody></table>\n</span>\n\n\n<!--\n}}}\n-->\n";config.shadowTiddlers["YourSearchItemTemplate"]="<!--\n{{{\n-->\n<span class='yourSearchNumber' macro='foundTiddler number'></span>\n<span class='yourSea"+"rchTitle' macro='foundTiddler title'/></span>&nbsp;-&nbsp;\n<span class='yourSearchTags' macro='found"+"Tiddler field tags 50'/></span>\n<span macro=\"yourSearch if previewText\"><div class='yourSearchText' macro='fo"+"undTiddler field text 250'/></div></span>\n<!--\n}}}\n-->";config.shadowTiddlers["YourSearch"]="<<tiddler [[YourSearch Help]]>>";config.shadowTiddlers["YourSearch Result"]="The popup-like window displaying the result of a YourSearch query.";config.macros.search.handler=_157;var _20a=function(){if(config.macros.search.handler!=_157){alert("Message from YourSearchPlugin:\n\n\nAnother plugin has disabled the 'Your Search' features.\n\n\nYou may "+"disable the other plugin or change the load order of \nthe plugins (by changing the names of the tidd"+"lers)\nto enable the 'Your Search' features.");}};setTimeout(_20a,5000);abego.YourSearch.getStandardRankFunction=function(){return _ff;};abego.YourSearch.getRankFunction=function(){return abego.YourSearch.getStandardRankFunction();};abego.YourSearch.getCurrentTiddler=function(){return _13c;};abego.YourSearch.closeResult=function(){_12a();};abego.YourSearch.getFoundTiddlers=function(){return _f2;};abego.YourSearch.getQuery=function(){return _f3;};abego.YourSearch.onShowResult=function(_20b){highlightHack=_f3?_f3.getMarkRegExp():null;if(!_20b){_13d.setItems(_f6());}
if(!_124){_124=createTiddlyElement(document.body,"div",_121,"yourSearchResult");}else{if(_124.parentNode!=document.body){document.body.appendChild(_124);}}
_146();highlightHack=null;};})();}
//%/
tstream is a tool for parsing and checking GXF files. You can download it by [[clicking here|zip/tstream.zip]].

You run tstream from a DOS window like this:
{{{
C:\temp>tstream
Usage:
tstream [/v] [/i] [/f] host cmd srcname dstname [option list]

for eg: tstream [/v] rfile name-of-the-file

 /v for verbose output
 /i create optional index file with retr, or use it with stor
 /f verifies FLT entries

The following commands are supported (all optional sizes are in bytes):
 retr.....retrieve srcname dstname [bufferSize] [windowSize]
 stor.....store srcname dstname [bufferSize] [windowSize]
 rfile....read stream_file [output_stream_file]...... parse it [and reform it] ...
 find.....read stream_file, parse each byte, profile headers found...
 help.....print this message

C:\temp>
}}}

''Example 1'': Verify that a GXF file is valid:

Here is a valid GXF file:
{{{
C:\temp>tstream rfile Clip.gxf
picture 180
parseFile: read 8155960 bytes
Num Packet of types: Map 2 EOF 1 UMF 4 FLT 1 media 131
}}}

Here is an invalid GXF file:
{{{
C:\temp>tstream rfile BadClip.gxf
picture 30 ERROR: unable to read 65536 payload bytes....

parseFile: read 2095200 bytes
}}}

''Example 2'': Split a GXF file into elementary files

Notice all elementary files with the names "newclip.*". newclip.M0 is the video track, newclip.A* are the audio tracks, newclip.T0 is the timecode track. The file "newclip" is the reassembled GXF file from the elementary files. It should match the original Clip.gxf file. There is also the XML representation of the clip in Clip.gxf.xml.
{{{
C:\temp>tstream rfile Clip.gxf newclip
picture 180
parseFile: read 8155960 bytes
Num Packet of types: Map 2 EOF 1 UMF 4 FLT 1 media 131

C:\temp>dir
Directory of C:\temp

04/28/2008 10:19 AM <DIR> .
04/22/2008 11:36 AM <DIR> ..
04/28/2008 08:50 AM 114,688 tstream.exe
12/07/2007 12:28 PM 8,155,960 Clip.gxf
04/28/2008 10:12 AM 83,542 Clip.gxf.xml
04/28/2008 10:19 AM 8,155,960 newclip
04/28/2008 10:19 AM 5,416,048 newclip.M0
04/28/2008 10:19 AM 327,680 newclip.A1
04/28/2008 10:19 AM 327,680 newclip.A2
04/28/2008 10:19 AM 327,680 newclip.A3
04/28/2008 10:19 AM 327,680 newclip.A4
04/28/2008 10:19 AM 327,680 newclip.A5
04/28/2008 10:19 AM 327,680 newclip.A6
04/28/2008 10:19 AM 327,680 newclip.A7
04/28/2008 10:19 AM 327,680 newclip.A8
04/28/2008 10:19 AM 0 newclip.T9
 14 File(s) 25,090,497 bytes
 2 Dir(s) 52,428,800,000 bytes free

}}}

''Example 3'': Show verbose GXF file details

{{{
C:\temp>tstream /v rfile Clip.gxf
--------reading header (offset 0)--------
stream header: 00 00 00 00 01 BC 00 00 02 C0 00 00 00 00 E1 E2
 Packet type = MAP
map->firstField = 0
map->lastField = 180
map->markIn = 0
map->markOut = 180
map->spaceEst = 7964 MB
map->status = 0
map->ctimeHi = 0
map->ctimeLo = 0
map->clipName = V:/default/Clip
map->uniqueName = (null)
map->setName = (null)
map->typeName = (null)
map->description= (null)
map->mediaFound = 1
map->nTracks = 10
map->Track[0]
 mapTrack->mediaName = Video_0.M0
 mapTrack->mediaVersion = 0
 mapTrack->mediaType = 11 MPEG525
 mapTrack->frameRate = 5 VIO_FR_30M
 mapTrack->linesPerFrame = 1 VIO_LINES_525
 mapTrack->fieldsPerFrame = 2 VIO_FPF_INTERLACED
 mapTrack->dropFrame = 0
 MPEG aux info:
 Bitrate: 25000000bps
 numIPerGop: 1
 Is16x9: 0 (Aspect Ratio 4x3)
 IsCBG: 0
 numPPerI: 3
 numBPerIorP: 1
 pixStruct: 0 PixStructureFrame
 chromaFmt: 2 MpegChroma422
 closedGop: 1 (Closed GOP)
 startLineNum: 13
 numLines16: 32 (Number of encoded video lines 512)
 validInfo: 1
 Progressive_sequence: 0
 field1OnTop: 1 (Top-field first)
map->Track[1]
 mapTrack->mediaName = Audio_0.A0
 mapTrack->mediaVersion = 0
 mapTrack->mediaType = 10 AUDIO_PCM16_MEDIA
 mapTrack->frameRate = 5 VIO_FR_30M
 mapTrack->linesPerFrame = 1 VIO_LINES_525
 mapTrack->fieldsPerFrame = 2 VIO_FPF_INTERLACED
 mapTrack->dropFrame = 0
 Audio aux info:
 First sample: 0
 Last sample: 14674
map->Track[2]
 mapTrack->mediaName = Audio_0.A1
 mapTrack->mediaVersion = 0
 mapTrack->mediaType = 10 AUDIO_PCM16_MEDIA
 mapTrack->frameRate = 5 VIO_FR_30M
 mapTrack->linesPerFrame = 1 VIO_LINES_525
 mapTrack->fieldsPerFrame = 2 VIO_FPF_INTERLACED
 mapTrack->dropFrame = 0
 Audio aux info:
 First sample: 0
 Last sample: 14674

etc.
}}}
In ~AppCenter from the System menu you can select a submenu called Export Log. This creates a zip file of the K2 Client's mlog.mmf and config.xml plus another file called version.txt. The version text file contains system information such as the software version, the system model, file system information, etc. Some developers have asked what calls we are making to generate this information. Below is a C# code snippet of the function and the calls that are used to generate this file.

{{{
[C#]
// function for generating version.txt file
private void CreateVersionFile()
{
    string platform = null;
    string model = null;
    string serialNum = null;
    string software = null;
    string realTime = null;
    string database = null;
    string fileSystem = null;
    string codecBoard = null;
 
    string filePath = Path.Combine(directory, VERSION);
 
    using ( StreamWriter sw = new StreamWriter( filePath ))
    {
        // Get the platform info
        appServerMgr.GetMSeriesIdInfo( IdComp.PLATFORM, out platform );

        if ( platform != null )
            sw.WriteLine( "Platform:  " + platform );

        // Get the model number
        appServerMgr.GetMSeriesIdInfo( IdComp.MODEL, out model );

        if ( model != null )
            sw.WriteLine( "Model:  " + model );

        // Get the serial number
        appServerMgr.GetMSeriesIdInfo( IdComp.SERIAL_NUM, out serialNum );

        if ( serialNum != null )
            sw.WriteLine( "Serial Number:  " + serialNum );

        // Get the software version
        appServerMgr.GetMSeriesIdInfo( IdComp.SW_VER, out software );

        if ( software != null )
            sw.WriteLine( "Software version:  " + software );

        // Get the real time version
        appServerMgr.GetMSeriesIdInfo( IdComp.RT_VER, out realTime );

        if ( realTime != null )
            sw.WriteLine( "Real time version:  " + realTime );

        // Get the database description
        appServerMgr.GetMSeriesIdInfo( IdComp.DB_DESC, out database );

        if ( database != null )
            sw.WriteLine( "Database description:  " + database );

        // Get the file system description
        appServerMgr.GetMSeriesIdInfo( IdComp.FS_DESC, out fileSystem );

        if ( fileSystem != null )
            sw.WriteLine( "File system description:  " + fileSystem );

        // Get the codec board
        appServerMgr.GetMSeriesIdInfo( IdComp.CODEC_BD_DESC, out codecBoard );

        if ( codecBoard != null )
            sw.WriteLine( "Codec board:  " + codecBoard );
    }
}
}}}