Compare commits

..

1 Commits

Author SHA1 Message Date
Steve Faulkner
fe92ec1e7c Use 2020-03-01 API version for ARM calls 2020-10-01 18:43:40 -05:00
215 changed files with 35034 additions and 7957 deletions

View File

@@ -105,6 +105,74 @@ jobs:
EMULATOR_ENDPOINT: https://0.0.0.0:8081/
NODE_TLS_REJECT_UNAUTHORIZED: 0
CYPRESS_CACHE_FOLDER: ~/.cache/Cypress
endtoendsql:
name: "End To End Tests | SQL"
needs: [lint, format, compile, unittest]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js 12.x
uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Restore Cypress Binary Cache
uses: actions/cache@v2
with:
path: ~/.cache/Cypress
key: ${{ runner.os }}-cypress-binary-cache
- run: npm ci
- name: End to End Tests
run: |
npm start &
cd cypress
npm ci
node cleanup.js
npm run wait-for-server
npx cypress run --browser chrome --headless --spec "./integration/dataexplorer/SQL/*"
shell: bash
env:
NODE_TLS_REJECT_UNAUTHORIZED: 0
CYPRESS_CACHE_FOLDER: ~/.cache/Cypress
CYPRESS_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_SQL }}
- uses: actions/upload-artifact@v2
name: videos
if: ${{ failure() }}
with:
path: "**/*.mp4"
endtoendmongo:
name: "End To End Tests | Mongo"
needs: [lint, format, compile, unittest]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js 12.x
uses: actions/setup-node@v1
with:
node-version: 12.x
- name: Restore Cypress Binary Cache
uses: actions/cache@v2
with:
path: ~/.cache/Cypress
key: ${{ runner.os }}-cypress-binary-cache
- name: End to End Tests
run: |
npm ci
npm start &
cd cypress
npm ci
node cleanup.js
npm run wait-for-server
npx cypress run --browser chrome --headless --spec "./integration/dataexplorer/MONGO/*"
shell: bash
env:
NODE_TLS_REJECT_UNAUTHORIZED: 0
CYPRESS_CACHE_FOLDER: ~/.cache/Cypress
CYPRESS_CONNECTION_STRING: ${{ secrets.CONNECTION_STRING_MONGO }}
- uses: actions/upload-artifact@v2
if: ${{ failure() }}
name: videos
with:
path: "**/*.mp4"
accessibility:
name: "Accessibility | Hosted"
needs: [lint, format, compile, unittest]
@@ -153,7 +221,7 @@ jobs:
nuget:
name: Publish Nuget
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendpuppeteer]
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendsql, endtoendmongo]
runs-on: ubuntu-latest
env:
NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }}
@@ -177,7 +245,7 @@ jobs:
nugetmpac:
name: Publish Nuget MPAC
if: github.ref == 'refs/heads/master' || contains(github.ref, 'hotfix/') || contains(github.ref, 'release/')
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendpuppeteer]
needs: [lint, format, compile, build, unittest, endtoendemulator, endtoendsql, endtoendmongo]
runs-on: ubuntu-latest
env:
NUGET_SOURCE: ${{ secrets.NUGET_SOURCE }}

View File

@@ -1,4 +1,4 @@
# Cosmos DB Explorer
# CosmosDB Explorer
UI for Azure Cosmos DB. Powers the [Azure Portal](https://portal.azure.com/), https://cosmos.azure.com/, and the [Cosmos DB Emulator](https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator)

View File

@@ -7,7 +7,7 @@ trigger:
- master
pool:
vmImage: "ubuntu-latest"
vmImage: 'ubuntu-latest'
steps:
- task: ComponentGovernanceComponentDetection@0
- task: ComponentGovernanceComponentDetection@0

177
externals/jquery.contextMenu.css vendored Normal file
View File

@@ -0,0 +1,177 @@
/*!
* jQuery contextMenu - Plugin for simple contextMenu handling
*
* Version: 1.6.6
*
* Authors: Rodney Rehm, Addy Osmani (patches for FF)
* Web: http://medialize.github.com/jQuery-contextMenu/
*
* Licensed under
* MIT License http://www.opensource.org/licenses/mit-license
* GPL v3 http://opensource.org/licenses/GPL-3.0
*
*/
.context-menu-list {
z-index: 1001;
position: fixed;
background: white;
border: solid 1px gainsboro;
box-shadow: 4px 4px 4px -2px #888888;
padding: 8px 0px 8px 0px;
line-height: 25px;
width: 254px;
list-style: none;
margin-left: -10px;
outline: 0px #fff;
}
.context-menu-item {
padding: 2px 2px 2px 31px;
background-color: #fff;
position: relative;
-webkit-user-select: none;
-moz-user-select: -moz-none;
-ms-user-select: none;
user-select: none;
}
.context-menu-separator {
padding-bottom: 0;
border-bottom: 1px solid #DDD;
}
.context-menu-item>label>input,
.context-menu-item>label>textarea {
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
margin-left: -10px;
}
.context-menu-item:hover {
cursor: pointer;
background-color: #eeeeee;
}
.context-menu-item.disabled {
color: #666;
}
.context-menu-input.hover,
.context-menu-item.disabled.hover {
cursor: default;
background-color: #EEE;
}
.context-menu-submenu:after {
content: ">";
color: #666;
position: absolute;
top: 0;
right: 3px;
z-index: 1;
}
/* icons
#protip:
In case you want to use sprites for icons (which I would suggest you do) have a look at
http://css-tricks.com/13224-pseudo-spriting/ to get an idea of how to implement
.context-menu-item.icon:before {}
*/
.context-menu-item.icon {
min-height: 18px;
background-repeat: no-repeat;
background-position: 10px 7px;
}
.context-menu-item.icon:hover {
min-height: 18px;
background-repeat: no-repeat;
background-position: 10px 7px;
}
/*.context-menu-item.icon-edit {
background-image: url(images/page_white_edit.png);
}
.context-menu-item.icon-cut {
background-image: url(images/cut.png);
}
.context-menu-item.icon-copy {
background-image: url(images/page_white_copy.png);
}
.context-menu-item.icon-paste {
background-image: url(images/page_white_paste.png);
}
.context-menu-item.icon-delete {
background-image: url(images/page_white_delete.png);
}
.context-menu-item.icon-add {
background-image: url(images/page_white_add.png);
}
.context-menu-item.icon-quit {
background-image: url(images/door.png);
}*/
/* vertically align inside labels */
.context-menu-input>label>* {
vertical-align: top;
}
/* position checkboxes and radios as icons */
.context-menu-input>label>input[type="checkbox"],
.context-menu-input>label>input[type="radio"] {
margin-left: -17px;
}
.context-menu-input>label>span {
margin-left: 5px;
}
.context-menu-input>label,
.context-menu-input>label>input[type="text"],
.context-menu-input>label>textarea,
.context-menu-input>label>select {
display: block;
width: 100%;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
}
.context-menu-input>label>textarea {
height: 100px;
}
.context-menu-item>.context-menu-list {
display: none;
/* re-positioned by js */
right: -5px;
top: 5px;
}
/*.context-menu-item.hover>.context-menu-list {
display: block;
padding-left: 5px;
}*/
.context-menu-accesskey {
text-decoration: underline;
}

1686
externals/jquery.contextMenu.js vendored Normal file

File diff suppressed because it is too large Load Diff

163
externals/jquery.dataTables.min.js vendored Normal file
View File

@@ -0,0 +1,163 @@
/*!
DataTables 1.10.9
©2008-2015 SpryMedia Ltd - datatables.net/license
*/
(function(Fa,T,k){var S=function(h){function X(a){var b,c,d={};h.each(a,function(e){if((b=e.match(/^([^A-Z]+?)([A-Z])/))&&-1!=="a aa ai ao as b fn i m o s ".indexOf(b[1]+" "))c=e.replace(b[0],b[2].toLowerCase()),d[c]=e,"o"===b[1]&&X(a[e])});a._hungarianMap=d}function I(a,b,c){a._hungarianMap||X(a);var d;h.each(b,function(e){d=a._hungarianMap[e];if(d!==k&&(c||b[d]===k))"o"===d.charAt(0)?(b[d]||(b[d]={}),h.extend(!0,b[d],b[e]),I(a[d],b[d],c)):b[d]=b[e]})}function S(a){var b=m.defaults.oLanguage,c=a.sZeroRecords;
!a.sEmptyTable&&(c&&"No data available in table"===b.sEmptyTable)&&F(a,a,"sZeroRecords","sEmptyTable");!a.sLoadingRecords&&(c&&"Loading..."===b.sLoadingRecords)&&F(a,a,"sZeroRecords","sLoadingRecords");a.sInfoThousands&&(a.sThousands=a.sInfoThousands);(a=a.sDecimal)&&cb(a)}function db(a){A(a,"ordering","bSort");A(a,"orderMulti","bSortMulti");A(a,"orderClasses","bSortClasses");A(a,"orderCellsTop","bSortCellsTop");A(a,"order","aaSorting");A(a,"orderFixed","aaSortingFixed");A(a,"paging","bPaginate");
A(a,"pagingType","sPaginationType");A(a,"pageLength","iDisplayLength");A(a,"searching","bFilter");"boolean"===typeof a.sScrollX&&(a.sScrollX=a.sScrollX?"100%":"");if(a=a.aoSearchCols)for(var b=0,c=a.length;b<c;b++)a[b]&&I(m.models.oSearch,a[b])}function eb(a){A(a,"orderable","bSortable");A(a,"orderData","aDataSort");A(a,"orderSequence","asSorting");A(a,"orderDataType","sortDataType");var b=a.aDataSort;b&&!h.isArray(b)&&(a.aDataSort=[b])}function fb(a){if(!m.__browser){var b={};m.__browser=b;var c=
h("<div/>").css({position:"fixed",top:0,left:0,height:1,width:1,overflow:"hidden"}).append(h("<div/>").css({position:"absolute",top:1,left:1,width:100,overflow:"scroll"}).append(h("<div/>").css({width:"100%",height:10}))).appendTo("body"),d=c.children(),e=d.children();b.barWidth=d[0].offsetWidth-d[0].clientWidth;b.bScrollOversize=100===e[0].offsetWidth&&100!==d[0].clientWidth;b.bScrollbarLeft=1!==Math.round(e.offset().left);b.bBounding=c[0].getBoundingClientRect().width?!0:!1;c.remove()}h.extend(a.oBrowser,
m.__browser);a.oScroll.iBarWidth=m.__browser.barWidth}function gb(a,b,c,d,e,f){var g,i=!1;c!==k&&(g=c,i=!0);for(;d!==e;)a.hasOwnProperty(d)&&(g=i?b(g,a[d],d,a):a[d],i=!0,d+=f);return g}function Ga(a,b){var c=m.defaults.column,d=a.aoColumns.length,c=h.extend({},m.models.oColumn,c,{nTh:b?b:T.createElement("th"),sTitle:c.sTitle?c.sTitle:b?b.innerHTML:"",aDataSort:c.aDataSort?c.aDataSort:[d],mData:c.mData?c.mData:d,idx:d});a.aoColumns.push(c);c=a.aoPreSearchCols;c[d]=h.extend({},m.models.oSearch,c[d]);
la(a,d,h(b).data())}function la(a,b,c){var b=a.aoColumns[b],d=a.oClasses,e=h(b.nTh);if(!b.sWidthOrig){b.sWidthOrig=e.attr("width")||null;var f=(e.attr("style")||"").match(/width:\s*(\d+[pxem%]+)/);f&&(b.sWidthOrig=f[1])}c!==k&&null!==c&&(eb(c),I(m.defaults.column,c),c.mDataProp!==k&&!c.mData&&(c.mData=c.mDataProp),c.sType&&(b._sManualType=c.sType),c.className&&!c.sClass&&(c.sClass=c.className),h.extend(b,c),F(b,c,"sWidth","sWidthOrig"),c.iDataSort!==k&&(b.aDataSort=[c.iDataSort]),F(b,c,"aDataSort"));
var g=b.mData,i=P(g),j=b.mRender?P(b.mRender):null,c=function(a){return"string"===typeof a&&-1!==a.indexOf("@")};b._bAttrSrc=h.isPlainObject(g)&&(c(g.sort)||c(g.type)||c(g.filter));b.fnGetData=function(a,b,c){var d=i(a,b,k,c);return j&&b?j(d,b,a,c):d};b.fnSetData=function(a,b,c){return Q(g)(a,b,c)};"number"!==typeof g&&(a._rowReadObject=!0);a.oFeatures.bSort||(b.bSortable=!1,e.addClass(d.sSortableNone));a=-1!==h.inArray("asc",b.asSorting);c=-1!==h.inArray("desc",b.asSorting);!b.bSortable||!a&&!c?
(b.sSortingClass=d.sSortableNone,b.sSortingClassJUI=""):a&&!c?(b.sSortingClass=d.sSortableAsc,b.sSortingClassJUI=d.sSortJUIAscAllowed):!a&&c?(b.sSortingClass=d.sSortableDesc,b.sSortingClassJUI=d.sSortJUIDescAllowed):(b.sSortingClass=d.sSortable,b.sSortingClassJUI=d.sSortJUI)}function Y(a){if(!1!==a.oFeatures.bAutoWidth){var b=a.aoColumns;Ha(a);for(var c=0,d=b.length;c<d;c++)b[c].nTh.style.width=b[c].sWidth}b=a.oScroll;(""!==b.sY||""!==b.sX)&&Z(a);w(a,null,"column-sizing",[a])}function $(a,b){var c=
aa(a,"bVisible");return"number"===typeof c[b]?c[b]:null}function ba(a,b){var c=aa(a,"bVisible"),c=h.inArray(b,c);return-1!==c?c:null}function ca(a){return aa(a,"bVisible").length}function aa(a,b){var c=[];h.map(a.aoColumns,function(a,e){a[b]&&c.push(e)});return c}function Ia(a){var b=a.aoColumns,c=a.aoData,d=m.ext.type.detect,e,f,g,i,j,h,l,r,q;e=0;for(f=b.length;e<f;e++)if(l=b[e],q=[],!l.sType&&l._sManualType)l.sType=l._sManualType;else if(!l.sType){g=0;for(i=d.length;g<i;g++){j=0;for(h=c.length;j<
h;j++){q[j]===k&&(q[j]=B(a,j,e,"type"));r=d[g](q[j],a);if(!r&&g!==d.length-1)break;if("html"===r)break}if(r){l.sType=r;break}}l.sType||(l.sType="string")}}function hb(a,b,c,d){var e,f,g,i,j,n,l=a.aoColumns;if(b)for(e=b.length-1;0<=e;e--){n=b[e];var r=n.targets!==k?n.targets:n.aTargets;h.isArray(r)||(r=[r]);f=0;for(g=r.length;f<g;f++)if("number"===typeof r[f]&&0<=r[f]){for(;l.length<=r[f];)Ga(a);d(r[f],n)}else if("number"===typeof r[f]&&0>r[f])d(l.length+r[f],n);else if("string"===typeof r[f]){i=0;
for(j=l.length;i<j;i++)("_all"==r[f]||h(l[i].nTh).hasClass(r[f]))&&d(i,n)}}if(c){e=0;for(a=c.length;e<a;e++)d(e,c[e])}}function L(a,b,c,d){var e=a.aoData.length,f=h.extend(!0,{},m.models.oRow,{src:c?"dom":"data",idx:e});f._aData=b;a.aoData.push(f);for(var g=a.aoColumns,i=0,j=g.length;i<j;i++)g[i].sType=null;a.aiDisplayMaster.push(e);b=a.rowIdFn(b);b!==k&&(a.aIds[b]=f);(c||!a.oFeatures.bDeferRender)&&Ja(a,e,c,d);return e}function ma(a,b){var c;b instanceof h||(b=h(b));return b.map(function(b,e){c=
Ka(a,e);return L(a,c.data,e,c.cells)})}function B(a,b,c,d){var e=a.iDraw,f=a.aoColumns[c],g=a.aoData[b]._aData,i=f.sDefaultContent,c=f.fnGetData(g,d,{settings:a,row:b,col:c});if(c===k)return a.iDrawError!=e&&null===i&&(J(a,0,"Requested unknown parameter "+("function"==typeof f.mData?"{function}":"'"+f.mData+"'")+" for row "+b,4),a.iDrawError=e),i;if((c===g||null===c)&&null!==i)c=i;else if("function"===typeof c)return c.call(g);return null===c&&"display"==d?"":c}function ib(a,b,c,d){a.aoColumns[c].fnSetData(a.aoData[b]._aData,
d,{settings:a,row:b,col:c})}function La(a){return h.map(a.match(/(\\.|[^\.])+/g)||[""],function(a){return a.replace(/\\./g,".")})}function P(a){if(h.isPlainObject(a)){var b={};h.each(a,function(a,c){c&&(b[a]=P(c))});return function(a,c,f,g){var i=b[c]||b._;return i!==k?i(a,c,f,g):a}}if(null===a)return function(a){return a};if("function"===typeof a)return function(b,c,f,g){return a(b,c,f,g)};if("string"===typeof a&&(-1!==a.indexOf(".")||-1!==a.indexOf("[")||-1!==a.indexOf("("))){var c=function(a,b,
f){var g,i;if(""!==f){i=La(f);for(var j=0,n=i.length;j<n;j++){f=i[j].match(da);g=i[j].match(U);if(f){i[j]=i[j].replace(da,"");""!==i[j]&&(a=a[i[j]]);g=[];i.splice(0,j+1);i=i.join(".");if(h.isArray(a)){j=0;for(n=a.length;j<n;j++)g.push(c(a[j],b,i))}a=f[0].substring(1,f[0].length-1);a=""===a?g:g.join(a);break}else if(g){i[j]=i[j].replace(U,"");a=a[i[j]]();continue}if(null===a||a[i[j]]===k)return k;a=a[i[j]]}}return a};return function(b,e){return c(b,e,a)}}return function(b){return b[a]}}function Q(a){if(h.isPlainObject(a))return Q(a._);
if(null===a)return function(){};if("function"===typeof a)return function(b,d,e){a(b,"set",d,e)};if("string"===typeof a&&(-1!==a.indexOf(".")||-1!==a.indexOf("[")||-1!==a.indexOf("("))){var b=function(a,d,e){var e=La(e),f;f=e[e.length-1];for(var g,i,j=0,n=e.length-1;j<n;j++){g=e[j].match(da);i=e[j].match(U);if(g){e[j]=e[j].replace(da,"");a[e[j]]=[];f=e.slice();f.splice(0,j+1);g=f.join(".");if(h.isArray(d)){i=0;for(n=d.length;i<n;i++)f={},b(f,d[i],g),a[e[j]].push(f)}else a[e[j]]=d;return}i&&(e[j]=e[j].replace(U,
""),a=a[e[j]](d));if(null===a[e[j]]||a[e[j]]===k)a[e[j]]={};a=a[e[j]]}if(f.match(U))a[f.replace(U,"")](d);else a[f.replace(da,"")]=d};return function(c,d){return b(c,d,a)}}return function(b,d){b[a]=d}}function Ma(a){return D(a.aoData,"_aData")}function na(a){a.aoData.length=0;a.aiDisplayMaster.length=0;a.aiDisplay.length=0;a.aIds={}}function oa(a,b,c){for(var d=-1,e=0,f=a.length;e<f;e++)a[e]==b?d=e:a[e]>b&&a[e]--; -1!=d&&c===k&&a.splice(d,1)}function ea(a,b,c,d){var e=a.aoData[b],f,g=function(c,d){for(;c.childNodes.length;)c.removeChild(c.firstChild);
c.innerHTML=B(a,b,d,"display")};if("dom"===c||(!c||"auto"===c)&&"dom"===e.src)e._aData=Ka(a,e,d,d===k?k:e._aData).data;else{var i=e.anCells;if(i)if(d!==k)g(i[d],d);else{c=0;for(f=i.length;c<f;c++)g(i[c],c)}}e._aSortData=null;e._aFilterData=null;g=a.aoColumns;if(d!==k)g[d].sType=null;else{c=0;for(f=g.length;c<f;c++)g[c].sType=null;Na(a,e)}}function Ka(a,b,c,d){var e=[],f=b.firstChild,g,i,j=0,n,l=a.aoColumns,r=a._rowReadObject,d=d!==k?d:r?{}:[],q=function(a,b){if("string"===typeof a){var c=a.indexOf("@");
-1!==c&&(c=a.substring(c+1),Q(a)(d,b.getAttribute(c)))}},jb=function(a){if(c===k||c===j)i=l[j],n=h.trim(a.innerHTML),i&&i._bAttrSrc?(Q(i.mData._)(d,n),q(i.mData.sort,a),q(i.mData.type,a),q(i.mData.filter,a)):r?(i._setter||(i._setter=Q(i.mData)),i._setter(d,n)):d[j]=n;j++};if(f)for(;f;){g=f.nodeName.toUpperCase();if("TD"==g||"TH"==g)jb(f),e.push(f);f=f.nextSibling}else{e=b.anCells;g=0;for(var o=e.length;g<o;g++)jb(e[g])}if(b=f?b:b.nTr)(b=b.getAttribute("id"))&&Q(a.rowId)(d,b);return{data:d,cells:e}}
function Ja(a,b,c,d){var e=a.aoData[b],f=e._aData,g=[],i,j,h,l,r;if(null===e.nTr){i=c||T.createElement("tr");e.nTr=i;e.anCells=g;i._DT_RowIndex=b;Na(a,e);l=0;for(r=a.aoColumns.length;l<r;l++){h=a.aoColumns[l];j=c?d[l]:T.createElement(h.sCellType);g.push(j);if(!c||h.mRender||h.mData!==l)j.innerHTML=B(a,b,l,"display");h.sClass&&(j.className+=" "+h.sClass);h.bVisible&&!c?i.appendChild(j):!h.bVisible&&c&&j.parentNode.removeChild(j);h.fnCreatedCell&&h.fnCreatedCell.call(a.oInstance,j,B(a,b,l),f,b,l)}w(a,
"aoRowCreatedCallback",null,[i,f,b])}e.nTr.setAttribute("role","row")}function Na(a,b){var c=b.nTr,d=b._aData;if(c){var e=a.rowIdFn(d);e&&(c.id=e);d.DT_RowClass&&(e=d.DT_RowClass.split(" "),b.__rowc=b.__rowc?pa(b.__rowc.concat(e)):e,h(c).removeClass(b.__rowc.join(" ")).addClass(d.DT_RowClass));d.DT_RowAttr&&h(c).attr(d.DT_RowAttr);d.DT_RowData&&h(c).data(d.DT_RowData)}}function kb(a){var b,c,d,e,f,g=a.nTHead,i=a.nTFoot,j=0===h("th, td",g).length,n=a.oClasses,l=a.aoColumns;j&&(e=h("<tr/>").appendTo(g));
b=0;for(c=l.length;b<c;b++)f=l[b],d=h(f.nTh).addClass(f.sClass),j&&d.appendTo(e),a.oFeatures.bSort&&(d.addClass(f.sSortingClass),!1!==f.bSortable&&(d.attr("tabindex",a.iTabIndex).attr("aria-controls",a.sTableId),Oa(a,f.nTh,b))),f.sTitle!=d[0].innerHTML&&d.html(f.sTitle),Pa(a,"header")(a,d,f,n);j&&fa(a.aoHeader,g);h(g).find(">tr").attr("role","row");h(g).find(">tr>th, >tr>td").addClass(n.sHeaderTH);h(i).find(">tr>th, >tr>td").addClass(n.sFooterTH);if(null!==i){a=a.aoFooter[0];b=0;for(c=a.length;b<
c;b++)f=l[b],f.nTf=a[b].cell,f.sClass&&h(f.nTf).addClass(f.sClass)}}function ga(a,b,c){var d,e,f,g=[],i=[],j=a.aoColumns.length,n;if(b){c===k&&(c=!1);d=0;for(e=b.length;d<e;d++){g[d]=b[d].slice();g[d].nTr=b[d].nTr;for(f=j-1;0<=f;f--)!a.aoColumns[f].bVisible&&!c&&g[d].splice(f,1);i.push([])}d=0;for(e=g.length;d<e;d++){if(a=g[d].nTr)for(;f=a.firstChild;)a.removeChild(f);f=0;for(b=g[d].length;f<b;f++)if(n=j=1,i[d][f]===k){a.appendChild(g[d][f].cell);for(i[d][f]=1;g[d+j]!==k&&g[d][f].cell==g[d+j][f].cell;)i[d+
j][f]=1,j++;for(;g[d][f+n]!==k&&g[d][f].cell==g[d][f+n].cell;){for(c=0;c<j;c++)i[d+c][f+n]=1;n++}h(g[d][f].cell).attr("rowspan",j).attr("colspan",n)}}}}function M(a){var b=w(a,"aoPreDrawCallback","preDraw",[a]);if(-1!==h.inArray(!1,b))C(a,!1);else{var b=[],c=0,d=a.asStripeClasses,e=d.length,f=a.oLanguage,g=a.iInitDisplayStart,i="ssp"==y(a),j=a.aiDisplay;a.bDrawing=!0;g!==k&&-1!==g&&(a._iDisplayStart=i?g:g>=a.fnRecordsDisplay()?0:g,a.iInitDisplayStart=-1);var g=a._iDisplayStart,n=a.fnDisplayEnd();
if(a.bDeferLoading)a.bDeferLoading=!1,a.iDraw++,C(a,!1);else if(i){if(!a.bDestroying&&!lb(a))return}else a.iDraw++;if(0!==j.length){f=i?a.aoData.length:n;for(i=i?0:g;i<f;i++){var l=j[i],r=a.aoData[l];null===r.nTr&&Ja(a,l);l=r.nTr;if(0!==e){var q=d[c%e];r._sRowStripe!=q&&(h(l).removeClass(r._sRowStripe).addClass(q),r._sRowStripe=q)}w(a,"aoRowCallback",null,[l,r._aData,c,i]);b.push(l);c++}}else c=f.sZeroRecords,1==a.iDraw&&"ajax"==y(a)?c=f.sLoadingRecords:f.sEmptyTable&&0===a.fnRecordsTotal()&&(c=f.sEmptyTable),
b[0]=h("<tr/>",{"class":e?d[0]:""}).append(h("<td />",{valign:"top",colSpan:ca(a),"class":a.oClasses.sRowEmpty}).html(c))[0];w(a,"aoHeaderCallback","header",[h(a.nTHead).children("tr")[0],Ma(a),g,n,j]);w(a,"aoFooterCallback","footer",[h(a.nTFoot).children("tr")[0],Ma(a),g,n,j]);d=h(a.nTBody);d.children().detach();d.append(h(b));w(a,"aoDrawCallback","draw",[a]);a.bSorted=!1;a.bFiltered=!1;a.bDrawing=!1}}function R(a,b){var c=a.oFeatures,d=c.bFilter;c.bSort&&mb(a);d?ha(a,a.oPreviousSearch):a.aiDisplay=
a.aiDisplayMaster.slice();!0!==b&&(a._iDisplayStart=0);a._drawHold=b;M(a);a._drawHold=!1}function nb(a){var b=a.oClasses,c=h(a.nTable),c=h("<div/>").insertBefore(c),d=a.oFeatures,e=h("<div/>",{id:a.sTableId+"_wrapper","class":b.sWrapper+(a.nTFoot?"":" "+b.sNoFooter)});a.nHolding=c[0];a.nTableWrapper=e[0];a.nTableReinsertBefore=a.nTable.nextSibling;for(var f=a.sDom.split(""),g,i,j,n,l,r,q=0;q<f.length;q++){g=null;i=f[q];if("<"==i){j=h("<div/>")[0];n=f[q+1];if("'"==n||'"'==n){l="";for(r=2;f[q+r]!=n;)l+=
f[q+r],r++;"H"==l?l=b.sJUIHeader:"F"==l&&(l=b.sJUIFooter);-1!=l.indexOf(".")?(n=l.split("."),j.id=n[0].substr(1,n[0].length-1),j.className=n[1]):"#"==l.charAt(0)?j.id=l.substr(1,l.length-1):j.className=l;q+=r}e.append(j);e=h(j)}else if(">"==i)e=e.parent();else if("l"==i&&d.bPaginate&&d.bLengthChange)g=ob(a);else if("f"==i&&d.bFilter)g=pb(a);else if("r"==i&&d.bProcessing)g=qb(a);else if("t"==i)g=rb(a);else if("i"==i&&d.bInfo)g=sb(a);else if("p"==i&&d.bPaginate)g=tb(a);else if(0!==m.ext.feature.length){j=
m.ext.feature;r=0;for(n=j.length;r<n;r++)if(i==j[r].cFeature){g=j[r].fnInit(a);break}}g&&(j=a.aanFeatures,j[i]||(j[i]=[]),j[i].push(g),e.append(g))}c.replaceWith(e);a.nHolding=null}function fa(a,b){var c=h(b).children("tr"),d,e,f,g,i,j,n,l,r,q;a.splice(0,a.length);f=0;for(j=c.length;f<j;f++)a.push([]);f=0;for(j=c.length;f<j;f++){d=c[f];for(e=d.firstChild;e;){if("TD"==e.nodeName.toUpperCase()||"TH"==e.nodeName.toUpperCase()){l=1*e.getAttribute("colspan");r=1*e.getAttribute("rowspan");l=!l||0===l||
1===l?1:l;r=!r||0===r||1===r?1:r;g=0;for(i=a[f];i[g];)g++;n=g;q=1===l?!0:!1;for(i=0;i<l;i++)for(g=0;g<r;g++)a[f+g][n+i]={cell:e,unique:q},a[f+g].nTr=d}e=e.nextSibling}}}function qa(a,b,c){var d=[];c||(c=a.aoHeader,b&&(c=[],fa(c,b)));for(var b=0,e=c.length;b<e;b++)for(var f=0,g=c[b].length;f<g;f++)if(c[b][f].unique&&(!d[f]||!a.bSortCellsTop))d[f]=c[b][f].cell;return d}function ra(a,b,c){w(a,"aoServerParams","serverParams",[b]);if(b&&h.isArray(b)){var d={},e=/(.*?)\[\]$/;h.each(b,function(a,b){var c=
b.name.match(e);c?(c=c[0],d[c]||(d[c]=[]),d[c].push(b.value)):d[b.name]=b.value});b=d}var f,g=a.ajax,i=a.oInstance,j=function(b){w(a,null,"xhr",[a,b,a.jqXHR]);c(b)};if(h.isPlainObject(g)&&g.data){f=g.data;var n=h.isFunction(f)?f(b,a):f,b=h.isFunction(f)&&n?n:h.extend(!0,b,n);delete g.data}n={data:b,success:function(b){var c=b.error||b.sError;c&&J(a,0,c);a.json=b;j(b)},dataType:"json",cache:!1,type:a.sServerMethod,error:function(b,c){var d=w(a,null,"xhr",[a,null,a.jqXHR]);-1===h.inArray(!0,d)&&("parsererror"==
c?J(a,0,"Invalid JSON response",1):4===b.readyState&&J(a,0,"Ajax error",7));C(a,!1)}};a.oAjaxData=b;w(a,null,"preXhr",[a,b]);a.fnServerData?a.fnServerData.call(i,a.sAjaxSource,h.map(b,function(a,b){return{name:b,value:a}}),j,a):a.sAjaxSource||"string"===typeof g?a.jqXHR=h.ajax(h.extend(n,{url:g||a.sAjaxSource})):h.isFunction(g)?a.jqXHR=g.call(i,b,j,a):(a.jqXHR=h.ajax(h.extend(n,g)),g.data=f)}function lb(a){return a.bAjaxDataGet?(a.iDraw++,C(a,!0),ra(a,ub(a),function(b){vb(a,b)}),!1):!0}function ub(a){var b=
a.aoColumns,c=b.length,d=a.oFeatures,e=a.oPreviousSearch,f=a.aoPreSearchCols,g,i=[],j,n,l,r=V(a);g=a._iDisplayStart;j=!1!==d.bPaginate?a._iDisplayLength:-1;var q=function(a,b){i.push({name:a,value:b})};q("sEcho",a.iDraw);q("iColumns",c);q("sColumns",D(b,"sName").join(","));q("iDisplayStart",g);q("iDisplayLength",j);var k={draw:a.iDraw,columns:[],order:[],start:g,length:j,search:{value:e.sSearch,regex:e.bRegex}};for(g=0;g<c;g++)n=b[g],l=f[g],j="function"==typeof n.mData?"function":n.mData,k.columns.push({data:j,
name:n.sName,searchable:n.bSearchable,orderable:n.bSortable,search:{value:l.sSearch,regex:l.bRegex}}),q("mDataProp_"+g,j),d.bFilter&&(q("sSearch_"+g,l.sSearch),q("bRegex_"+g,l.bRegex),q("bSearchable_"+g,n.bSearchable)),d.bSort&&q("bSortable_"+g,n.bSortable);d.bFilter&&(q("sSearch",e.sSearch),q("bRegex",e.bRegex));d.bSort&&(h.each(r,function(a,b){k.order.push({column:b.col,dir:b.dir});q("iSortCol_"+a,b.col);q("sSortDir_"+a,b.dir)}),q("iSortingCols",r.length));b=m.ext.legacy.ajax;return null===b?a.sAjaxSource?
i:k:b?i:k}function vb(a,b){var c=sa(a,b),d=b.sEcho!==k?b.sEcho:b.draw,e=b.iTotalRecords!==k?b.iTotalRecords:b.recordsTotal,f=b.iTotalDisplayRecords!==k?b.iTotalDisplayRecords:b.recordsFiltered;if(d){if(1*d<a.iDraw)return;a.iDraw=1*d}na(a);a._iRecordsTotal=parseInt(e,10);a._iRecordsDisplay=parseInt(f,10);d=0;for(e=c.length;d<e;d++)L(a,c[d]);a.aiDisplay=a.aiDisplayMaster.slice();a.bAjaxDataGet=!1;M(a);a._bInitComplete||ta(a,b);a.bAjaxDataGet=!0;C(a,!1)}function sa(a,b){var c=h.isPlainObject(a.ajax)&&
a.ajax.dataSrc!==k?a.ajax.dataSrc:a.sAjaxDataProp;return"data"===c?b.aaData||b[c]:""!==c?P(c)(b):b}function pb(a){var b=a.oClasses,c=a.sTableId,d=a.oLanguage,e=a.oPreviousSearch,f=a.aanFeatures,g='<input type="search" class="'+b.sFilterInput+'"/>',i=d.sSearch,i=i.match(/_INPUT_/)?i.replace("_INPUT_",g):i+g,b=h("<div/>",{id:!f.f?c+"_filter":null,"class":b.sFilter}).append(h("<label/>").append(i)),f=function(){var b=!this.value?"":this.value;b!=e.sSearch&&(ha(a,{sSearch:b,bRegex:e.bRegex,bSmart:e.bSmart,
bCaseInsensitive:e.bCaseInsensitive}),a._iDisplayStart=0,M(a))},g=null!==a.searchDelay?a.searchDelay:"ssp"===y(a)?400:0,j=h("input",b).val(e.sSearch).attr("placeholder",d.sSearchPlaceholder).bind("keyup.DT search.DT input.DT paste.DT cut.DT",g?ua(f,g):f).bind("keypress.DT",function(a){if(13==a.keyCode)return!1}).attr("aria-controls",c);h(a.nTable).on("search.dt.DT",function(b,c){if(a===c)try{j[0]!==T.activeElement&&j.val(e.sSearch)}catch(d){}});return b[0]}function ha(a,b,c){var d=a.oPreviousSearch,
e=a.aoPreSearchCols,f=function(a){d.sSearch=a.sSearch;d.bRegex=a.bRegex;d.bSmart=a.bSmart;d.bCaseInsensitive=a.bCaseInsensitive};Ia(a);if("ssp"!=y(a)){wb(a,b.sSearch,c,b.bEscapeRegex!==k?!b.bEscapeRegex:b.bRegex,b.bSmart,b.bCaseInsensitive);f(b);for(b=0;b<e.length;b++)xb(a,e[b].sSearch,b,e[b].bEscapeRegex!==k?!e[b].bEscapeRegex:e[b].bRegex,e[b].bSmart,e[b].bCaseInsensitive);yb(a)}else f(b);a.bFiltered=!0;w(a,null,"search",[a])}function yb(a){for(var b=m.ext.search,c=a.aiDisplay,d,e,f=0,g=b.length;f<
g;f++){for(var i=[],j=0,n=c.length;j<n;j++)e=c[j],d=a.aoData[e],b[f](a,d._aFilterData,e,d._aData,j)&&i.push(e);c.length=0;h.merge(c,i)}}function xb(a,b,c,d,e,f){if(""!==b)for(var g=a.aiDisplay,d=Qa(b,d,e,f),e=g.length-1;0<=e;e--)b=a.aoData[g[e]]._aFilterData[c],d.test(b)||g.splice(e,1)}function wb(a,b,c,d,e,f){var d=Qa(b,d,e,f),e=a.oPreviousSearch.sSearch,f=a.aiDisplayMaster,g;0!==m.ext.search.length&&(c=!0);g=zb(a);if(0>=b.length)a.aiDisplay=f.slice();else{if(g||c||e.length>b.length||0!==b.indexOf(e)||
a.bSorted)a.aiDisplay=f.slice();b=a.aiDisplay;for(c=b.length-1;0<=c;c--)d.test(a.aoData[b[c]]._sFilterRow)||b.splice(c,1)}}function Qa(a,b,c,d){a=b?a:va(a);c&&(a="^(?=.*?"+h.map(a.match(/"[^"]+"|[^ ]+/g)||[""],function(a){if('"'===a.charAt(0))var b=a.match(/^"(.*)"$/),a=b?b[1]:a;return a.replace('"',"")}).join(")(?=.*?")+").*$");return RegExp(a,d?"i":"")}function va(a){return a.replace(Yb,"\\$1")}function zb(a){var b=a.aoColumns,c,d,e,f,g,i,j,h,l=m.ext.type.search;c=!1;d=0;for(f=a.aoData.length;d<
f;d++)if(h=a.aoData[d],!h._aFilterData){i=[];e=0;for(g=b.length;e<g;e++)c=b[e],c.bSearchable?(j=B(a,d,e,"filter"),l[c.sType]&&(j=l[c.sType](j)),null===j&&(j=""),"string"!==typeof j&&j.toString&&(j=j.toString())):j="",j.indexOf&&-1!==j.indexOf("&")&&(wa.innerHTML=j,j=Zb?wa.textContent:wa.innerText),j.replace&&(j=j.replace(/[\r\n]/g,"")),i.push(j);h._aFilterData=i;h._sFilterRow=i.join(" ");c=!0}return c}function Ab(a){return{search:a.sSearch,smart:a.bSmart,regex:a.bRegex,caseInsensitive:a.bCaseInsensitive}}
function Bb(a){return{sSearch:a.search,bSmart:a.smart,bRegex:a.regex,bCaseInsensitive:a.caseInsensitive}}function sb(a){var b=a.sTableId,c=a.aanFeatures.i,d=h("<div/>",{"class":a.oClasses.sInfo,id:!c?b+"_info":null});c||(a.aoDrawCallback.push({fn:Cb,sName:"information"}),d.attr("role","status").attr("aria-live","polite"),h(a.nTable).attr("aria-describedby",b+"_info"));return d[0]}function Cb(a){var b=a.aanFeatures.i;if(0!==b.length){var c=a.oLanguage,d=a._iDisplayStart+1,e=a.fnDisplayEnd(),f=a.fnRecordsTotal(),
g=a.fnRecordsDisplay(),i=g?c.sInfo:c.sInfoEmpty;g!==f&&(i+=" "+c.sInfoFiltered);i+=c.sInfoPostFix;i=Db(a,i);c=c.fnInfoCallback;null!==c&&(i=c.call(a.oInstance,a,d,e,f,g,i));h(b).html(i)}}function Db(a,b){var c=a.fnFormatNumber,d=a._iDisplayStart+1,e=a._iDisplayLength,f=a.fnRecordsDisplay(),g=-1===e;return b.replace(/_START_/g,c.call(a,d)).replace(/_END_/g,c.call(a,a.fnDisplayEnd())).replace(/_MAX_/g,c.call(a,a.fnRecordsTotal())).replace(/_TOTAL_/g,c.call(a,f)).replace(/_PAGE_/g,c.call(a,g?1:Math.ceil(d/
e))).replace(/_PAGES_/g,c.call(a,g?1:Math.ceil(f/e)))}function ia(a){var b,c,d=a.iInitDisplayStart,e=a.aoColumns,f;c=a.oFeatures;var g=a.bDeferLoading;if(a.bInitialised){nb(a);kb(a);ga(a,a.aoHeader);ga(a,a.aoFooter);C(a,!0);c.bAutoWidth&&Ha(a);b=0;for(c=e.length;b<c;b++)f=e[b],f.sWidth&&(f.nTh.style.width=u(f.sWidth));w(a,null,"preInit",[a]);R(a);e=y(a);if("ssp"!=e||g)"ajax"==e?ra(a,[],function(c){var f=sa(a,c);for(b=0;b<f.length;b++)L(a,f[b]);a.iInitDisplayStart=d;R(a);C(a,!1);ta(a,c)},a):(C(a,!1),
ta(a))}else setTimeout(function(){ia(a)},200)}function ta(a,b){a._bInitComplete=!0;(b||a.oInit.aaData)&&Y(a);w(a,"aoInitComplete","init",[a,b])}function Ra(a,b){var c=parseInt(b,10);a._iDisplayLength=c;Sa(a);w(a,null,"length",[a,c])}function ob(a){for(var b=a.oClasses,c=a.sTableId,d=a.aLengthMenu,e=h.isArray(d[0]),f=e?d[0]:d,d=e?d[1]:d,e=h("<select/>",{name:c+"_length","aria-controls":c,"class":b.sLengthSelect}),g=0,i=f.length;g<i;g++)e[0][g]=new Option(d[g],f[g]);var j=h("<div><label/></div>").addClass(b.sLength);
a.aanFeatures.l||(j[0].id=c+"_length");j.children().append(a.oLanguage.sLengthMenu.replace("_MENU_",e[0].outerHTML));h("select",j).val(a._iDisplayLength).bind("change.DT",function(){Ra(a,h(this).val());M(a)});h(a.nTable).bind("length.dt.DT",function(b,c,d){a===c&&h("select",j).val(d)});return j[0]}function tb(a){var b=a.sPaginationType,c=m.ext.pager[b],d="function"===typeof c,e=function(a){M(a)},b=h("<div/>").addClass(a.oClasses.sPaging+b)[0],f=a.aanFeatures;d||c.fnInit(a,b,e);f.p||(b.id=a.sTableId+
"_paginate",a.aoDrawCallback.push({fn:function(a){if(d){var b=a._iDisplayStart,j=a._iDisplayLength,h=a.fnRecordsDisplay(),l=-1===j,b=l?0:Math.ceil(b/j),j=l?1:Math.ceil(h/j),h=c(b,j),k,l=0;for(k=f.p.length;l<k;l++)Pa(a,"pageButton")(a,f.p[l],l,h,b,j)}else c.fnUpdate(a,e)},sName:"pagination"}));return b}function Ta(a,b,c){var d=a._iDisplayStart,e=a._iDisplayLength,f=a.fnRecordsDisplay();0===f||-1===e?d=0:"number"===typeof b?(d=b*e,d>f&&(d=0)):"first"==b?d=0:"previous"==b?(d=0<=e?d-e:0,0>d&&(d=0)):"next"==
b?d+e<f&&(d+=e):"last"==b?d=Math.floor((f-1)/e)*e:J(a,0,"Unknown paging action: "+b,5);b=a._iDisplayStart!==d;a._iDisplayStart=d;b&&(w(a,null,"page",[a]),c&&M(a));return b}function qb(a){return h("<div/>",{id:!a.aanFeatures.r?a.sTableId+"_processing":null,"class":a.oClasses.sProcessing}).html(a.oLanguage.sProcessing).insertBefore(a.nTable)[0]}function C(a,b){a.oFeatures.bProcessing&&h(a.aanFeatures.r).css("display",b?"block":"none");w(a,null,"processing",[a,b])}function rb(a){var b=h(a.nTable);b.attr("role",
"grid");var c=a.oScroll;if(""===c.sX&&""===c.sY)return a.nTable;var d=c.sX,e=c.sY,f=a.oClasses,g=b.children("caption"),i=g.length?g[0]._captionSide:null,j=h(b[0].cloneNode(!1)),n=h(b[0].cloneNode(!1)),l=b.children("tfoot");c.sX&&"100%"===b.attr("width")&&b.removeAttr("width");l.length||(l=null);j=h("<div/>",{"class":f.sScrollWrapper}).append(h("<div/>",{"class":f.sScrollHead}).css({overflow:"hidden",position:"relative",border:0,width:d?!d?null:u(d):"100%"}).append(h("<div/>",{"class":f.sScrollHeadInner}).css({"box-sizing":"content-box",
width:c.sXInner||"100%"}).append(j.removeAttr("id").css("margin-left",0).append("top"===i?g:null).append(b.children("thead"))))).append(h("<div/>",{"class":f.sScrollBody}).css({position:"relative",overflow:"auto",width:!d?null:u(d)}).append(b));l&&j.append(h("<div/>",{"class":f.sScrollFoot}).css({overflow:"hidden",border:0,width:d?!d?null:u(d):"100%"}).append(h("<div/>",{"class":f.sScrollFootInner}).append(n.removeAttr("id").css("margin-left",0).append("bottom"===i?g:null).append(b.children("tfoot")))));
var b=j.children(),k=b[0],f=b[1],q=l?b[2]:null;if(d)h(f).on("scroll.DT",function(){var a=this.scrollLeft;k.scrollLeft=a;l&&(q.scrollLeft=a)});h(f).css(e&&c.bCollapse?"max-height":"height",e);a.nScrollHead=k;a.nScrollBody=f;a.nScrollFoot=q;a.aoDrawCallback.push({fn:Z,sName:"scrolling"});return j[0]}function Z(a){var b=a.oScroll,c=b.sX,d=b.sXInner,e=b.sY,b=b.iBarWidth,f=h(a.nScrollHead),g=f[0].style,i=f.children("div"),j=i[0].style,n=i.children("table"),i=a.nScrollBody,l=h(i),k=i.style,q=h(a.nScrollFoot).children("div"),
m=q.children("table"),o=h(a.nTHead),E=h(a.nTable),p=E[0],t=p.style,N=a.nTFoot?h(a.nTFoot):null,Eb=a.oBrowser,w=Eb.bScrollOversize,s,v,O,x,y=[],z=[],A=[],B,C=function(a){a=a.style;a.paddingTop="0";a.paddingBottom="0";a.borderTopWidth="0";a.borderBottomWidth="0";a.height=0};E.children("thead, tfoot").remove();x=o.clone().prependTo(E);o=o.find("tr");v=x.find("tr");x.find("th, td").removeAttr("tabindex");N&&(O=N.clone().prependTo(E),s=N.find("tr"),O=O.find("tr"));c||(k.width="100%",f[0].style.width="100%");
h.each(qa(a,x),function(b,c){B=$(a,b);c.style.width=a.aoColumns[B].sWidth});N&&H(function(a){a.style.width=""},O);f=E.outerWidth();if(""===c){t.width="100%";if(w&&(E.find("tbody").height()>i.offsetHeight||"scroll"==l.css("overflow-y")))t.width=u(E.outerWidth()-b);f=E.outerWidth()}else""!==d&&(t.width=u(d),f=E.outerWidth());H(C,v);H(function(a){A.push(a.innerHTML);y.push(u(h(a).css("width")))},v);H(function(a,b){a.style.width=y[b]},o);h(v).height(0);N&&(H(C,O),H(function(a){z.push(u(h(a).css("width")))},
O),H(function(a,b){a.style.width=z[b]},s),h(O).height(0));H(function(a,b){a.innerHTML='<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+A[b]+"</div>";a.style.width=y[b]},v);N&&H(function(a,b){a.innerHTML="";a.style.width=z[b]},O);if(E.outerWidth()<f){s=i.scrollHeight>i.offsetHeight||"scroll"==l.css("overflow-y")?f+b:f;if(w&&(i.scrollHeight>i.offsetHeight||"scroll"==l.css("overflow-y")))t.width=u(s-b);(""===c||""!==d)&&J(a,1,"Possible column misalignment",6)}else s="100%";k.width=
u(s);g.width=u(s);N&&(a.nScrollFoot.style.width=u(s));!e&&w&&(k.height=u(p.offsetHeight+b));c=E.outerWidth();n[0].style.width=u(c);j.width=u(c);d=E.height()>i.clientHeight||"scroll"==l.css("overflow-y");e="padding"+(Eb.bScrollbarLeft?"Left":"Right");j[e]=d?b+"px":"0px";N&&(m[0].style.width=u(c),q[0].style.width=u(c),q[0].style[e]=d?b+"px":"0px");l.scroll();if((a.bSorted||a.bFiltered)&&!a._drawHold)i.scrollTop=0}function H(a,b,c){for(var d=0,e=0,f=b.length,g,i;e<f;){g=b[e].firstChild;for(i=c?c[e].firstChild:
null;g;)1===g.nodeType&&(c?a(g,i,d):a(g,d),d++),g=g.nextSibling,i=c?i.nextSibling:null;e++}}function Ha(a){var b=a.nTable,c=a.aoColumns,d=a.oScroll,e=d.sY,f=d.sX,g=d.sXInner,i=c.length,j=aa(a,"bVisible"),n=h("th",a.nTHead),l=b.getAttribute("width"),k=b.parentNode,q=!1,m,o,p;p=a.oBrowser;d=p.bScrollOversize;(m=b.style.width)&&-1!==m.indexOf("%")&&(l=m);for(m=0;m<j.length;m++)o=c[j[m]],null!==o.sWidth&&(o.sWidth=Fb(o.sWidthOrig,k),q=!0);if(d||!q&&!f&&!e&&i==ca(a)&&i==n.length)for(m=0;m<i;m++){if(j=
$(a,m))c[j].sWidth=u(n.eq(m).width())}else{i=h(b).clone().css("visibility","hidden").removeAttr("id");i.find("tbody tr").remove();var t=h("<tr/>").appendTo(i.find("tbody"));i.find("thead, tfoot").remove();i.append(h(a.nTHead).clone()).append(h(a.nTFoot).clone());i.find("tfoot th, tfoot td").css("width","");n=qa(a,i.find("thead")[0]);for(m=0;m<j.length;m++)o=c[j[m]],n[m].style.width=null!==o.sWidthOrig&&""!==o.sWidthOrig?u(o.sWidthOrig):"";if(a.aoData.length)for(m=0;m<j.length;m++)q=j[m],o=c[q],h(Gb(a,
q)).clone(!1).append(o.sContentPadding).appendTo(t);q=h("<div/>").css(f||e?{position:"absolute",top:0,left:0,height:1,right:0,overflow:"hidden"}:{}).append(i).appendTo(k);f&&g?i.width(g):f?(i.css("width","auto"),i.width()<k.clientWidth&&i.width(k.clientWidth)):e?i.width(k.clientWidth):l&&i.width(l);if(f){for(m=g=0;m<j.length;m++)o=c[j[m]],e=p.bBounding?n[m].getBoundingClientRect().width:h(n[m]).outerWidth(),g+=null===o.sWidthOrig?e:parseInt(o.sWidth,10)+e-h(n[m]).width();i.width(u(g));b.style.width=
u(g)}for(m=0;m<j.length;m++)if(o=c[j[m]],p=h(n[m]).width())o.sWidth=u(p);b.style.width=u(i.css("width"));q.remove()}l&&(b.style.width=u(l));if((l||f)&&!a._reszEvt)b=function(){h(Fa).bind("resize.DT-"+a.sInstance,ua(function(){Y(a)}))},d?setTimeout(b,1E3):b(),a._reszEvt=!0}function ua(a,b){var c=b!==k?b:200,d,e;return function(){var b=this,g=+new Date,i=arguments;d&&g<d+c?(clearTimeout(e),e=setTimeout(function(){d=k;a.apply(b,i)},c)):(d=g,a.apply(b,i))}}function Fb(a,b){if(!a)return 0;var c=h("<div/>").css("width",
u(a)).appendTo(b||T.body),d=c[0].offsetWidth;c.remove();return d}function Gb(a,b){var c=Hb(a,b);if(0>c)return null;var d=a.aoData[c];return!d.nTr?h("<td/>").html(B(a,c,b,"display"))[0]:d.anCells[b]}function Hb(a,b){for(var c,d=-1,e=-1,f=0,g=a.aoData.length;f<g;f++)c=B(a,f,b,"display")+"",c=c.replace($b,""),c.length>d&&(d=c.length,e=f);return e}function u(a){return null===a?"0px":"number"==typeof a?0>a?"0px":a+"px":a.match(/\d$/)?a+"px":a}function V(a){var b,c,d=[],e=a.aoColumns,f,g,i,j;b=a.aaSortingFixed;
c=h.isPlainObject(b);var n=[];f=function(a){a.length&&!h.isArray(a[0])?n.push(a):h.merge(n,a)};h.isArray(b)&&f(b);c&&b.pre&&f(b.pre);f(a.aaSorting);c&&b.post&&f(b.post);for(a=0;a<n.length;a++){j=n[a][0];f=e[j].aDataSort;b=0;for(c=f.length;b<c;b++)g=f[b],i=e[g].sType||"string",n[a]._idx===k&&(n[a]._idx=h.inArray(n[a][1],e[g].asSorting)),d.push({src:j,col:g,dir:n[a][1],index:n[a]._idx,type:i,formatter:m.ext.type.order[i+"-pre"]})}return d}function mb(a){var b,c,d=[],e=m.ext.type.order,f=a.aoData,g=
0,i,j=a.aiDisplayMaster,h;Ia(a);h=V(a);b=0;for(c=h.length;b<c;b++)i=h[b],i.formatter&&g++,Ib(a,i.col);if("ssp"!=y(a)&&0!==h.length){b=0;for(c=j.length;b<c;b++)d[j[b]]=b;g===h.length?j.sort(function(a,b){var c,e,g,i,j=h.length,k=f[a]._aSortData,m=f[b]._aSortData;for(g=0;g<j;g++)if(i=h[g],c=k[i.col],e=m[i.col],c=c<e?-1:c>e?1:0,0!==c)return"asc"===i.dir?c:-c;c=d[a];e=d[b];return c<e?-1:c>e?1:0}):j.sort(function(a,b){var c,g,i,j,k=h.length,m=f[a]._aSortData,p=f[b]._aSortData;for(i=0;i<k;i++)if(j=h[i],
c=m[j.col],g=p[j.col],j=e[j.type+"-"+j.dir]||e["string-"+j.dir],c=j(c,g),0!==c)return c;c=d[a];g=d[b];return c<g?-1:c>g?1:0})}a.bSorted=!0}function Jb(a){for(var b,c,d=a.aoColumns,e=V(a),a=a.oLanguage.oAria,f=0,g=d.length;f<g;f++){c=d[f];var i=c.asSorting;b=c.sTitle.replace(/<.*?>/g,"");var j=c.nTh;j.removeAttribute("aria-sort");c.bSortable&&(0<e.length&&e[0].col==f?(j.setAttribute("aria-sort","asc"==e[0].dir?"ascending":"descending"),c=i[e[0].index+1]||i[0]):c=i[0],b+="asc"===c?a.sSortAscending:
a.sSortDescending);j.setAttribute("aria-label",b)}}function Ua(a,b,c,d){var e=a.aaSorting,f=a.aoColumns[b].asSorting,g=function(a,b){var c=a._idx;c===k&&(c=h.inArray(a[1],f));return c+1<f.length?c+1:b?null:0};"number"===typeof e[0]&&(e=a.aaSorting=[e]);c&&a.oFeatures.bSortMulti?(c=h.inArray(b,D(e,"0")),-1!==c?(b=g(e[c],!0),null===b&&1===e.length&&(b=0),null===b?e.splice(c,1):(e[c][1]=f[b],e[c]._idx=b)):(e.push([b,f[0],0]),e[e.length-1]._idx=0)):e.length&&e[0][0]==b?(b=g(e[0]),e.length=1,e[0][1]=f[b],
e[0]._idx=b):(e.length=0,e.push([b,f[0]]),e[0]._idx=0);R(a);"function"==typeof d&&d(a)}function Oa(a,b,c,d){var e=a.aoColumns[c];Va(b,{},function(b){!1!==e.bSortable&&(a.oFeatures.bProcessing?(C(a,!0),setTimeout(function(){Ua(a,c,b.shiftKey,d);"ssp"!==y(a)&&C(a,!1)},0)):Ua(a,c,b.shiftKey,d))})}function xa(a){var b=a.aLastSort,c=a.oClasses.sSortColumn,d=V(a),e=a.oFeatures,f,g;if(e.bSort&&e.bSortClasses){e=0;for(f=b.length;e<f;e++)g=b[e].src,h(D(a.aoData,"anCells",g)).removeClass(c+(2>e?e+1:3));e=0;
for(f=d.length;e<f;e++)g=d[e].src,h(D(a.aoData,"anCells",g)).addClass(c+(2>e?e+1:3))}a.aLastSort=d}function Ib(a,b){var c=a.aoColumns[b],d=m.ext.order[c.sSortDataType],e;d&&(e=d.call(a.oInstance,a,b,ba(a,b)));for(var f,g=m.ext.type.order[c.sType+"-pre"],i=0,h=a.aoData.length;i<h;i++)if(c=a.aoData[i],c._aSortData||(c._aSortData=[]),!c._aSortData[b]||d)f=d?e[i]:B(a,i,b,"sort"),c._aSortData[b]=g?g(f):f}function ya(a){if(a.oFeatures.bStateSave&&!a.bDestroying){var b={time:+new Date,start:a._iDisplayStart,
length:a._iDisplayLength,order:h.extend(!0,[],a.aaSorting),search:Ab(a.oPreviousSearch),columns:h.map(a.aoColumns,function(b,d){return{visible:b.bVisible,search:Ab(a.aoPreSearchCols[d])}})};w(a,"aoStateSaveParams","stateSaveParams",[a,b]);a.oSavedState=b;a.fnStateSaveCallback.call(a.oInstance,a,b)}}function Kb(a){var b,c,d=a.aoColumns;if(a.oFeatures.bStateSave){var e=a.fnStateLoadCallback.call(a.oInstance,a);if(e&&e.time&&(b=w(a,"aoStateLoadParams","stateLoadParams",[a,e]),-1===h.inArray(!1,b)&&(b=
a.iStateDuration,!(0<b&&e.time<+new Date-1E3*b)&&d.length===e.columns.length))){a.oLoadedState=h.extend(!0,{},e);e.start!==k&&(a._iDisplayStart=e.start,a.iInitDisplayStart=e.start);e.length!==k&&(a._iDisplayLength=e.length);e.order!==k&&(a.aaSorting=[],h.each(e.order,function(b,c){a.aaSorting.push(c[0]>=d.length?[0,c[1]]:c)}));e.search!==k&&h.extend(a.oPreviousSearch,Bb(e.search));b=0;for(c=e.columns.length;b<c;b++){var f=e.columns[b];f.visible!==k&&(d[b].bVisible=f.visible);f.search!==k&&h.extend(a.aoPreSearchCols[b],
Bb(f.search))}w(a,"aoStateLoaded","stateLoaded",[a,e])}}}function za(a){var b=m.settings,a=h.inArray(a,D(b,"nTable"));return-1!==a?b[a]:null}function J(a,b,c,d){c="DataTables warning: "+(a?"table id="+a.sTableId+" - ":"")+c;d&&(c+=". For more information about this error, please see http://datatables.net/tn/"+d);if(b)Fa.console&&console.log&&console.log(c);else if(b=m.ext,b=b.sErrMode||b.errMode,a&&w(a,null,"error",[a,d,c]),"alert"==b)alert(c);else{if("throw"==b)throw Error(c);"function"==typeof b&&
b(a,d,c)}}function F(a,b,c,d){h.isArray(c)?h.each(c,function(c,d){h.isArray(d)?F(a,b,d[0],d[1]):F(a,b,d)}):(d===k&&(d=c),b[c]!==k&&(a[d]=b[c]))}function Lb(a,b,c){var d,e;for(e in b)b.hasOwnProperty(e)&&(d=b[e],h.isPlainObject(d)?(h.isPlainObject(a[e])||(a[e]={}),h.extend(!0,a[e],d)):a[e]=c&&"data"!==e&&"aaData"!==e&&h.isArray(d)?d.slice():d);return a}function Va(a,b,c){h(a).bind("click.DT",b,function(b){a.blur();c(b)}).bind("keypress.DT",b,function(a){13===a.which&&(a.preventDefault(),c(a))}).bind("selectstart.DT",
function(){return!1})}function z(a,b,c,d){c&&a[b].push({fn:c,sName:d})}function w(a,b,c,d){var e=[];b&&(e=h.map(a[b].slice().reverse(),function(b){return b.fn.apply(a.oInstance,d)}));null!==c&&(b=h.Event(c+".dt"),h(a.nTable).trigger(b,d),e.push(b.result));return e}function Sa(a){var b=a._iDisplayStart,c=a.fnDisplayEnd(),d=a._iDisplayLength;b>=c&&(b=c-d);b-=b%d;if(-1===d||0>b)b=0;a._iDisplayStart=b}function Pa(a,b){var c=a.renderer,d=m.ext.renderer[b];return h.isPlainObject(c)&&c[b]?d[c[b]]||d._:"string"===
typeof c?d[c]||d._:d._}function y(a){return a.oFeatures.bServerSide?"ssp":a.ajax||a.sAjaxSource?"ajax":"dom"}function Aa(a,b){var c=[],c=Mb.numbers_length,d=Math.floor(c/2);b<=c?c=W(0,b):a<=d?(c=W(0,c-2),c.push("ellipsis"),c.push(b-1)):(a>=b-1-d?c=W(b-(c-2),b):(c=W(a-d+2,a+d-1),c.push("ellipsis"),c.push(b-1)),c.splice(0,0,"ellipsis"),c.splice(0,0,0));c.DT_el="span";return c}function cb(a){h.each({num:function(b){return Ba(b,a)},"num-fmt":function(b){return Ba(b,a,Wa)},"html-num":function(b){return Ba(b,
a,Ca)},"html-num-fmt":function(b){return Ba(b,a,Ca,Wa)}},function(b,c){v.type.order[b+a+"-pre"]=c;b.match(/^html\-/)&&(v.type.search[b+a]=v.type.search.html)})}function Nb(a){return function(){var b=[za(this[m.ext.iApiIndex])].concat(Array.prototype.slice.call(arguments));return m.ext.internal[a].apply(this,b)}}var m,v,t,p,s,Xa={},Ob=/[\r\n]/g,Ca=/<.*?>/g,ac=/^[\w\+\-]/,bc=/[\w\+\-]$/,Yb=RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\|\\$|\\^|\\-)","g"),Wa=/[',$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi,
K=function(a){return!a||!0===a||"-"===a?!0:!1},Pb=function(a){var b=parseInt(a,10);return!isNaN(b)&&isFinite(a)?b:null},Qb=function(a,b){Xa[b]||(Xa[b]=RegExp(va(b),"g"));return"string"===typeof a&&"."!==b?a.replace(/\./g,"").replace(Xa[b],"."):a},Ya=function(a,b,c){var d="string"===typeof a;if(K(a))return!0;b&&d&&(a=Qb(a,b));c&&d&&(a=a.replace(Wa,""));return!isNaN(parseFloat(a))&&isFinite(a)},Rb=function(a,b,c){return K(a)?!0:!(K(a)||"string"===typeof a)?null:Ya(a.replace(Ca,""),b,c)?!0:null},D=function(a,
b,c){var d=[],e=0,f=a.length;if(c!==k)for(;e<f;e++)a[e]&&a[e][b]&&d.push(a[e][b][c]);else for(;e<f;e++)a[e]&&d.push(a[e][b]);return d},ja=function(a,b,c,d){var e=[],f=0,g=b.length;if(d!==k)for(;f<g;f++)a[b[f]][c]&&e.push(a[b[f]][c][d]);else for(;f<g;f++)e.push(a[b[f]][c]);return e},W=function(a,b){var c=[],d;b===k?(b=0,d=a):(d=b,b=a);for(var e=b;e<d;e++)c.push(e);return c},Sb=function(a){for(var b=[],c=0,d=a.length;c<d;c++)a[c]&&b.push(a[c]);return b},pa=function(a){var b=[],c,d,e=a.length,f,g=0;
d=0;a:for(;d<e;d++){c=a[d];for(f=0;f<g;f++)if(b[f]===c)continue a;b.push(c);g++}return b},A=function(a,b,c){a[b]!==k&&(a[c]=a[b])},da=/\[.*?\]$/,U=/\(\)$/,wa=h("<div>")[0],Zb=wa.textContent!==k,$b=/<.*?>/g;m=function(a){this.$=function(a,b){return this.api(!0).$(a,b)};this._=function(a,b){return this.api(!0).rows(a,b).data()};this.api=function(a){return a?new t(za(this[v.iApiIndex])):new t(this)};this.fnAddData=function(a,b){var c=this.api(!0),d=h.isArray(a)&&(h.isArray(a[0])||h.isPlainObject(a[0]))?
c.rows.add(a):c.row.add(a);(b===k||b)&&c.draw();return d.flatten().toArray()};this.fnAdjustColumnSizing=function(a){var b=this.api(!0).columns.adjust(),c=b.settings()[0],d=c.oScroll;a===k||a?b.draw(!1):(""!==d.sX||""!==d.sY)&&Z(c)};this.fnClearTable=function(a){var b=this.api(!0).clear();(a===k||a)&&b.draw()};this.fnClose=function(a){this.api(!0).row(a).child.hide()};this.fnDeleteRow=function(a,b,c){var d=this.api(!0),a=d.rows(a),e=a.settings()[0],h=e.aoData[a[0][0]];a.remove();b&&b.call(this,e,h);
(c===k||c)&&d.draw();return h};this.fnDestroy=function(a){this.api(!0).destroy(a)};this.fnDraw=function(a){this.api(!0).draw(a)};this.fnFilter=function(a,b,c,d,e,h){e=this.api(!0);null===b||b===k?e.search(a,c,d,h):e.column(b).search(a,c,d,h);e.draw()};this.fnGetData=function(a,b){var c=this.api(!0);if(a!==k){var d=a.nodeName?a.nodeName.toLowerCase():"";return b!==k||"td"==d||"th"==d?c.cell(a,b).data():c.row(a).data()||null}return c.data().toArray()};this.fnGetNodes=function(a){var b=this.api(!0);
return a!==k?b.row(a).node():b.rows().nodes().flatten().toArray()};this.fnGetPosition=function(a){var b=this.api(!0),c=a.nodeName.toUpperCase();return"TR"==c?b.row(a).index():"TD"==c||"TH"==c?(a=b.cell(a).index(),[a.row,a.columnVisible,a.column]):null};this.fnIsOpen=function(a){return this.api(!0).row(a).child.isShown()};this.fnOpen=function(a,b,c){return this.api(!0).row(a).child(b,c).show().child()[0]};this.fnPageChange=function(a,b){var c=this.api(!0).page(a);(b===k||b)&&c.draw(!1)};this.fnSetColumnVis=
function(a,b,c){a=this.api(!0).column(a).visible(b);(c===k||c)&&a.columns.adjust().draw()};this.fnSettings=function(){return za(this[v.iApiIndex])};this.fnSort=function(a){this.api(!0).order(a).draw()};this.fnSortListener=function(a,b,c){this.api(!0).order.listener(a,b,c)};this.fnUpdate=function(a,b,c,d,e){var h=this.api(!0);c===k||null===c?h.row(b).data(a):h.cell(b,c).data(a);(e===k||e)&&h.columns.adjust();(d===k||d)&&h.draw();return 0};this.fnVersionCheck=v.fnVersionCheck;var b=this,c=a===k,d=this.length;
c&&(a={});this.oApi=this.internal=v.internal;for(var e in m.ext.internal)e&&(this[e]=Nb(e));this.each(function(){var e={},e=1<d?Lb(e,a,!0):a,g=0,i,j=this.getAttribute("id"),n=!1,l=m.defaults,r=h(this);if("table"!=this.nodeName.toLowerCase())J(null,0,"Non-table node initialisation ("+this.nodeName+")",2);else{db(l);eb(l.column);I(l,l,!0);I(l.column,l.column,!0);I(l,h.extend(e,r.data()));var q=m.settings,g=0;for(i=q.length;g<i;g++){var p=q[g];if(p.nTable==this||p.nTHead.parentNode==this||p.nTFoot&&
p.nTFoot.parentNode==this){g=e.bRetrieve!==k?e.bRetrieve:l.bRetrieve;if(c||g)return p.oInstance;if(e.bDestroy!==k?e.bDestroy:l.bDestroy){p.oInstance.fnDestroy();break}else{J(p,0,"Cannot reinitialise DataTable",3);return}}if(p.sTableId==this.id){q.splice(g,1);break}}if(null===j||""===j)this.id=j="DataTables_Table_"+m.ext._unique++;var o=h.extend(!0,{},m.models.oSettings,{sDestroyWidth:r[0].style.width,sInstance:j,sTableId:j});o.nTable=this;o.oApi=b.internal;o.oInit=e;q.push(o);o.oInstance=1===b.length?
b:r.dataTable();db(e);e.oLanguage&&S(e.oLanguage);e.aLengthMenu&&!e.iDisplayLength&&(e.iDisplayLength=h.isArray(e.aLengthMenu[0])?e.aLengthMenu[0][0]:e.aLengthMenu[0]);e=Lb(h.extend(!0,{},l),e);F(o.oFeatures,e,"bPaginate bLengthChange bFilter bSort bSortMulti bInfo bProcessing bAutoWidth bSortClasses bServerSide bDeferRender".split(" "));F(o,e,["asStripeClasses","ajax","fnServerData","fnFormatNumber","sServerMethod","aaSorting","aaSortingFixed","aLengthMenu","sPaginationType","sAjaxSource","sAjaxDataProp",
"iStateDuration","sDom","bSortCellsTop","iTabIndex","fnStateLoadCallback","fnStateSaveCallback","renderer","searchDelay","rowId",["iCookieDuration","iStateDuration"],["oSearch","oPreviousSearch"],["aoSearchCols","aoPreSearchCols"],["iDisplayLength","_iDisplayLength"],["bJQueryUI","bJUI"]]);F(o.oScroll,e,[["sScrollX","sX"],["sScrollXInner","sXInner"],["sScrollY","sY"],["bScrollCollapse","bCollapse"]]);F(o.oLanguage,e,"fnInfoCallback");z(o,"aoDrawCallback",e.fnDrawCallback,"user");z(o,"aoServerParams",
e.fnServerParams,"user");z(o,"aoStateSaveParams",e.fnStateSaveParams,"user");z(o,"aoStateLoadParams",e.fnStateLoadParams,"user");z(o,"aoStateLoaded",e.fnStateLoaded,"user");z(o,"aoRowCallback",e.fnRowCallback,"user");z(o,"aoRowCreatedCallback",e.fnCreatedRow,"user");z(o,"aoHeaderCallback",e.fnHeaderCallback,"user");z(o,"aoFooterCallback",e.fnFooterCallback,"user");z(o,"aoInitComplete",e.fnInitComplete,"user");z(o,"aoPreDrawCallback",e.fnPreDrawCallback,"user");o.rowIdFn=P(e.rowId);fb(o);j=o.oClasses;
e.bJQueryUI?(h.extend(j,m.ext.oJUIClasses,e.oClasses),e.sDom===l.sDom&&"lfrtip"===l.sDom&&(o.sDom='<"H"lfr>t<"F"ip>'),o.renderer)?h.isPlainObject(o.renderer)&&!o.renderer.header&&(o.renderer.header="jqueryui"):o.renderer="jqueryui":h.extend(j,m.ext.classes,e.oClasses);r.addClass(j.sTable);o.iInitDisplayStart===k&&(o.iInitDisplayStart=e.iDisplayStart,o._iDisplayStart=e.iDisplayStart);null!==e.iDeferLoading&&(o.bDeferLoading=!0,g=h.isArray(e.iDeferLoading),o._iRecordsDisplay=g?e.iDeferLoading[0]:e.iDeferLoading,
o._iRecordsTotal=g?e.iDeferLoading[1]:e.iDeferLoading);var t=o.oLanguage;h.extend(!0,t,e.oLanguage);""!==t.sUrl&&(h.ajax({dataType:"json",url:t.sUrl,success:function(a){S(a);I(l.oLanguage,a);h.extend(true,t,a);ia(o)},error:function(){ia(o)}}),n=!0);null===e.asStripeClasses&&(o.asStripeClasses=[j.sStripeOdd,j.sStripeEven]);var g=o.asStripeClasses,s=r.children("tbody").find("tr").eq(0);-1!==h.inArray(!0,h.map(g,function(a){return s.hasClass(a)}))&&(h("tbody tr",this).removeClass(g.join(" ")),o.asDestroyStripes=
g.slice());q=[];g=this.getElementsByTagName("thead");0!==g.length&&(fa(o.aoHeader,g[0]),q=qa(o));if(null===e.aoColumns){p=[];g=0;for(i=q.length;g<i;g++)p.push(null)}else p=e.aoColumns;g=0;for(i=p.length;g<i;g++)Ga(o,q?q[g]:null);hb(o,e.aoColumnDefs,p,function(a,b){la(o,a,b)});if(s.length){var u=function(a,b){return a.getAttribute("data-"+b)!==null?b:null};h(s[0]).children("th, td").each(function(a,b){var c=o.aoColumns[a];if(c.mData===a){var d=u(b,"sort")||u(b,"order"),e=u(b,"filter")||u(b,"search");
if(d!==null||e!==null){c.mData={_:a+".display",sort:d!==null?a+".@data-"+d:k,type:d!==null?a+".@data-"+d:k,filter:e!==null?a+".@data-"+e:k};la(o,a)}}})}var v=o.oFeatures;e.bStateSave&&(v.bStateSave=!0,Kb(o,e),z(o,"aoDrawCallback",ya,"state_save"));if(e.aaSorting===k){q=o.aaSorting;g=0;for(i=q.length;g<i;g++)q[g][1]=o.aoColumns[g].asSorting[0]}xa(o);v.bSort&&z(o,"aoDrawCallback",function(){if(o.bSorted){var a=V(o),b={};h.each(a,function(a,c){b[c.src]=c.dir});w(o,null,"order",[o,a,b]);Jb(o)}});z(o,
"aoDrawCallback",function(){(o.bSorted||y(o)==="ssp"||v.bDeferRender)&&xa(o)},"sc");g=r.children("caption").each(function(){this._captionSide=r.css("caption-side")});i=r.children("thead");0===i.length&&(i=h("<thead/>").appendTo(this));o.nTHead=i[0];i=r.children("tbody");0===i.length&&(i=h("<tbody/>").appendTo(this));o.nTBody=i[0];i=r.children("tfoot");if(0===i.length&&0<g.length&&(""!==o.oScroll.sX||""!==o.oScroll.sY))i=h("<tfoot/>").appendTo(this);0===i.length||0===i.children().length?r.addClass(j.sNoFooter):
0<i.length&&(o.nTFoot=i[0],fa(o.aoFooter,o.nTFoot));if(e.aaData)for(g=0;g<e.aaData.length;g++)L(o,e.aaData[g]);else(o.bDeferLoading||"dom"==y(o))&&ma(o,h(o.nTBody).children("tr"));o.aiDisplay=o.aiDisplayMaster.slice();o.bInitialised=!0;!1===n&&ia(o)}});b=null;return this};var Tb=[],x=Array.prototype,cc=function(a){var b,c,d=m.settings,e=h.map(d,function(a){return a.nTable});if(a){if(a.nTable&&a.oApi)return[a];if(a.nodeName&&"table"===a.nodeName.toLowerCase())return b=h.inArray(a,e),-1!==b?[d[b]]:
null;if(a&&"function"===typeof a.settings)return a.settings().toArray();"string"===typeof a?c=h(a):a instanceof h&&(c=a)}else return[];if(c)return c.map(function(){b=h.inArray(this,e);return-1!==b?d[b]:null}).toArray()};t=function(a,b){if(!(this instanceof t))return new t(a,b);var c=[],d=function(a){(a=cc(a))&&(c=c.concat(a))};if(h.isArray(a))for(var e=0,f=a.length;e<f;e++)d(a[e]);else d(a);this.context=pa(c);b&&h.merge(this,b);this.selector={rows:null,cols:null,opts:null};t.extend(this,this,Tb)};
m.Api=t;h.extend(t.prototype,{any:function(){return 0!==this.count()},concat:x.concat,context:[],count:function(){return this.flatten().length},each:function(a){for(var b=0,c=this.length;b<c;b++)a.call(this,this[b],b,this);return this},eq:function(a){var b=this.context;return b.length>a?new t(b[a],this[a]):null},filter:function(a){var b=[];if(x.filter)b=x.filter.call(this,a,this);else for(var c=0,d=this.length;c<d;c++)a.call(this,this[c],c,this)&&b.push(this[c]);return new t(this.context,b)},flatten:function(){var a=
[];return new t(this.context,a.concat.apply(a,this.toArray()))},join:x.join,indexOf:x.indexOf||function(a,b){for(var c=b||0,d=this.length;c<d;c++)if(this[c]===a)return c;return-1},iterator:function(a,b,c,d){var e=[],f,g,h,j,n,l=this.context,m,q,p=this.selector;"string"===typeof a&&(d=c,c=b,b=a,a=!1);g=0;for(h=l.length;g<h;g++){var o=new t(l[g]);if("table"===b)f=c.call(o,l[g],g),f!==k&&e.push(f);else if("columns"===b||"rows"===b)f=c.call(o,l[g],this[g],g),f!==k&&e.push(f);else if("column"===b||"column-rows"===
b||"row"===b||"cell"===b){q=this[g];"column-rows"===b&&(m=Da(l[g],p.opts));j=0;for(n=q.length;j<n;j++)f=q[j],f="cell"===b?c.call(o,l[g],f.row,f.column,g,j):c.call(o,l[g],f,g,j,m),f!==k&&e.push(f)}}return e.length||d?(a=new t(l,a?e.concat.apply([],e):e),b=a.selector,b.rows=p.rows,b.cols=p.cols,b.opts=p.opts,a):this},lastIndexOf:x.lastIndexOf||function(a,b){return this.indexOf.apply(this.toArray.reverse(),arguments)},length:0,map:function(a){var b=[];if(x.map)b=x.map.call(this,a,this);else for(var c=
0,d=this.length;c<d;c++)b.push(a.call(this,this[c],c));return new t(this.context,b)},pluck:function(a){return this.map(function(b){return b[a]})},pop:x.pop,push:x.push,reduce:x.reduce||function(a,b){return gb(this,a,b,0,this.length,1)},reduceRight:x.reduceRight||function(a,b){return gb(this,a,b,this.length-1,-1,-1)},reverse:x.reverse,selector:null,shift:x.shift,sort:x.sort,splice:x.splice,toArray:function(){return x.slice.call(this)},to$:function(){return h(this)},toJQuery:function(){return h(this)},
unique:function(){return new t(this.context,pa(this))},unshift:x.unshift});t.extend=function(a,b,c){if(c.length&&b&&(b instanceof t||b.__dt_wrapper)){var d,e,f,g=function(a,b,c){return function(){var d=b.apply(a,arguments);t.extend(d,d,c.methodExt);return d}};d=0;for(e=c.length;d<e;d++)f=c[d],b[f.name]="function"===typeof f.val?g(a,f.val,f):h.isPlainObject(f.val)?{}:f.val,b[f.name].__dt_wrapper=!0,t.extend(a,b[f.name],f.propExt)}};t.register=p=function(a,b){if(h.isArray(a))for(var c=0,d=a.length;c<
d;c++)t.register(a[c],b);else for(var e=a.split("."),f=Tb,g,i,c=0,d=e.length;c<d;c++){g=(i=-1!==e[c].indexOf("()"))?e[c].replace("()",""):e[c];var j;a:{j=0;for(var n=f.length;j<n;j++)if(f[j].name===g){j=f[j];break a}j=null}j||(j={name:g,val:{},methodExt:[],propExt:[]},f.push(j));c===d-1?j.val=b:f=i?j.methodExt:j.propExt}};t.registerPlural=s=function(a,b,c){t.register(a,c);t.register(b,function(){var a=c.apply(this,arguments);return a===this?this:a instanceof t?a.length?h.isArray(a[0])?new t(a.context,
a[0]):a[0]:k:a})};p("tables()",function(a){var b;if(a){b=t;var c=this.context;if("number"===typeof a)a=[c[a]];else var d=h.map(c,function(a){return a.nTable}),a=h(d).filter(a).map(function(){var a=h.inArray(this,d);return c[a]}).toArray();b=new b(a)}else b=this;return b});p("table()",function(a){var a=this.tables(a),b=a.context;return b.length?new t(b[0]):a});s("tables().nodes()","table().node()",function(){return this.iterator("table",function(a){return a.nTable},1)});s("tables().body()","table().body()",
function(){return this.iterator("table",function(a){return a.nTBody},1)});s("tables().header()","table().header()",function(){return this.iterator("table",function(a){return a.nTHead},1)});s("tables().footer()","table().footer()",function(){return this.iterator("table",function(a){return a.nTFoot},1)});s("tables().containers()","table().container()",function(){return this.iterator("table",function(a){return a.nTableWrapper},1)});p("draw()",function(a){return this.iterator("table",function(b){"page"===
a?M(b):("string"===typeof a&&(a="full-hold"===a?!1:!0),R(b,!1===a))})});p("page()",function(a){return a===k?this.page.info().page:this.iterator("table",function(b){Ta(b,a)})});p("page.info()",function(){if(0===this.context.length)return k;var a=this.context[0],b=a._iDisplayStart,c=a._iDisplayLength,d=a.fnRecordsDisplay(),e=-1===c;return{page:e?0:Math.floor(b/c),pages:e?1:Math.ceil(d/c),start:b,end:a.fnDisplayEnd(),length:c,recordsTotal:a.fnRecordsTotal(),recordsDisplay:d,serverSide:"ssp"===y(a)}});
p("page.len()",function(a){return a===k?0!==this.context.length?this.context[0]._iDisplayLength:k:this.iterator("table",function(b){Ra(b,a)})});var Ub=function(a,b,c){if(c){var d=new t(a);d.one("draw",function(){c(d.ajax.json())})}if("ssp"==y(a))R(a,b);else{C(a,!0);var e=a.jqXHR;e&&4!==e.readyState&&e.abort();ra(a,[],function(c){na(a);for(var c=sa(a,c),d=0,e=c.length;d<e;d++)L(a,c[d]);R(a,b);C(a,!1)})}};p("ajax.json()",function(){var a=this.context;if(0<a.length)return a[0].json});p("ajax.params()",
function(){var a=this.context;if(0<a.length)return a[0].oAjaxData});p("ajax.reload()",function(a,b){return this.iterator("table",function(c){Ub(c,!1===b,a)})});p("ajax.url()",function(a){var b=this.context;if(a===k){if(0===b.length)return k;b=b[0];return b.ajax?h.isPlainObject(b.ajax)?b.ajax.url:b.ajax:b.sAjaxSource}return this.iterator("table",function(b){h.isPlainObject(b.ajax)?b.ajax.url=a:b.ajax=a})});p("ajax.url().load()",function(a,b){return this.iterator("table",function(c){Ub(c,!1===b,a)})});
var Za=function(a,b,c,d,e){var f=[],g,i,j,n,l,m;j=typeof b;if(!b||"string"===j||"function"===j||b.length===k)b=[b];j=0;for(n=b.length;j<n;j++){i=b[j]&&b[j].split?b[j].split(","):[b[j]];l=0;for(m=i.length;l<m;l++)(g=c("string"===typeof i[l]?h.trim(i[l]):i[l]))&&g.length&&(f=f.concat(g))}a=v.selector[a];if(a.length){j=0;for(n=a.length;j<n;j++)f=a[j](d,e,f)}return pa(f)},$a=function(a){a||(a={});a.filter&&a.search===k&&(a.search=a.filter);return h.extend({search:"none",order:"current",page:"all"},a)},
ab=function(a){for(var b=0,c=a.length;b<c;b++)if(0<a[b].length)return a[0]=a[b],a[0].length=1,a.length=1,a.context=[a.context[b]],a;a.length=0;return a},Da=function(a,b){var c,d,e,f=[],g=a.aiDisplay;c=a.aiDisplayMaster;var i=b.search;d=b.order;e=b.page;if("ssp"==y(a))return"removed"===i?[]:W(0,c.length);if("current"==e){c=a._iDisplayStart;for(d=a.fnDisplayEnd();c<d;c++)f.push(g[c])}else if("current"==d||"applied"==d)f="none"==i?c.slice():"applied"==i?g.slice():h.map(c,function(a){return-1===h.inArray(a,
g)?a:null});else if("index"==d||"original"==d){c=0;for(d=a.aoData.length;c<d;c++)"none"==i?f.push(c):(e=h.inArray(c,g),(-1===e&&"removed"==i||0<=e&&"applied"==i)&&f.push(c))}return f};p("rows()",function(a,b){a===k?a="":h.isPlainObject(a)&&(b=a,a="");var b=$a(b),c=this.iterator("table",function(c){var e=b;return Za("row",a,function(a){var b=Pb(a);if(b!==null&&!e)return[b];var i=Da(c,e);if(b!==null&&h.inArray(b,i)!==-1)return[b];if(!a)return i;if(typeof a==="function")return h.map(i,function(b){var e=
c.aoData[b];return a(b,e._aData,e.nTr)?b:null});b=Sb(ja(c.aoData,i,"nTr"));if(a.nodeName&&h.inArray(a,b)!==-1)return[a._DT_RowIndex];if(typeof a==="string"&&a.charAt(0)==="#"){i=c.aIds[a.replace(/^#/,"")];if(i!==k)return[i.idx]}return h(b).filter(a).map(function(){return this._DT_RowIndex}).toArray()},c,e)},1);c.selector.rows=a;c.selector.opts=b;return c});p("rows().nodes()",function(){return this.iterator("row",function(a,b){return a.aoData[b].nTr||k},1)});p("rows().data()",function(){return this.iterator(!0,
"rows",function(a,b){return ja(a.aoData,b,"_aData")},1)});s("rows().cache()","row().cache()",function(a){return this.iterator("row",function(b,c){var d=b.aoData[c];return"search"===a?d._aFilterData:d._aSortData},1)});s("rows().invalidate()","row().invalidate()",function(a){return this.iterator("row",function(b,c){ea(b,c,a)})});s("rows().indexes()","row().index()",function(){return this.iterator("row",function(a,b){return b},1)});s("rows().ids()","row().id()",function(a){for(var b=[],c=this.context,
d=0,e=c.length;d<e;d++)for(var f=0,g=this[d].length;f<g;f++){var h=c[d].rowIdFn(c[d].aoData[this[d][f]]._aData);b.push((!0===a?"#":"")+h)}return new t(c,b)});s("rows().remove()","row().remove()",function(){var a=this;this.iterator("row",function(b,c,d){var e=b.aoData,f=e[c];e.splice(c,1);for(var g=0,h=e.length;g<h;g++)null!==e[g].nTr&&(e[g].nTr._DT_RowIndex=g);oa(b.aiDisplayMaster,c);oa(b.aiDisplay,c);oa(a[d],c,!1);Sa(b);c=b.rowIdFn(f._aData);c!==k&&delete b.aIds[c]});this.iterator("table",function(a){for(var c=
0,d=a.aoData.length;c<d;c++)a.aoData[c].idx=c});return this});p("rows.add()",function(a){var b=this.iterator("table",function(b){var c,f,g,h=[];f=0;for(g=a.length;f<g;f++)c=a[f],c.nodeName&&"TR"===c.nodeName.toUpperCase()?h.push(ma(b,c)[0]):h.push(L(b,c));return h},1),c=this.rows(-1);c.pop();h.merge(c,b);return c});p("row()",function(a,b){return ab(this.rows(a,b))});p("row().data()",function(a){var b=this.context;if(a===k)return b.length&&this.length?b[0].aoData[this[0]]._aData:k;b[0].aoData[this[0]]._aData=
a;ea(b[0],this[0],"data");return this});p("row().node()",function(){var a=this.context;return a.length&&this.length?a[0].aoData[this[0]].nTr||null:null});p("row.add()",function(a){a instanceof h&&a.length&&(a=a[0]);var b=this.iterator("table",function(b){return a.nodeName&&"TR"===a.nodeName.toUpperCase()?ma(b,a)[0]:L(b,a)});return this.row(b[0])});var bb=function(a,b){var c=a.context;if(c.length&&(c=c[0].aoData[b!==k?b:a[0]])&&c._details)c._details.remove(),c._detailsShow=k,c._details=k},Vb=function(a,
b){var c=a.context;if(c.length&&a.length){var d=c[0].aoData[a[0]];if(d._details){(d._detailsShow=b)?d._details.insertAfter(d.nTr):d._details.detach();var e=c[0],f=new t(e),g=e.aoData;f.off("draw.dt.DT_details column-visibility.dt.DT_details destroy.dt.DT_details");0<D(g,"_details").length&&(f.on("draw.dt.DT_details",function(a,b){e===b&&f.rows({page:"current"}).eq(0).each(function(a){a=g[a];a._detailsShow&&a._details.insertAfter(a.nTr)})}),f.on("column-visibility.dt.DT_details",function(a,b){if(e===
b)for(var c,d=ca(b),f=0,h=g.length;f<h;f++)c=g[f],c._details&&c._details.children("td[colspan]").attr("colspan",d)}),f.on("destroy.dt.DT_details",function(a,b){if(e===b)for(var c=0,d=g.length;c<d;c++)g[c]._details&&bb(f,c)}))}}};p("row().child()",function(a,b){var c=this.context;if(a===k)return c.length&&this.length?c[0].aoData[this[0]]._details:k;if(!0===a)this.child.show();else if(!1===a)bb(this);else if(c.length&&this.length){var d=c[0],c=c[0].aoData[this[0]],e=[],f=function(a,b){if(h.isArray(a)||
a instanceof h)for(var c=0,k=a.length;c<k;c++)f(a[c],b);else a.nodeName&&"tr"===a.nodeName.toLowerCase()?e.push(a):(c=h("<tr><td/></tr>").addClass(b),h("td",c).addClass(b).html(a)[0].colSpan=ca(d),e.push(c[0]))};f(a,b);c._details&&c._details.remove();c._details=h(e);c._detailsShow&&c._details.insertAfter(c.nTr)}return this});p(["row().child.show()","row().child().show()"],function(){Vb(this,!0);return this});p(["row().child.hide()","row().child().hide()"],function(){Vb(this,!1);return this});p(["row().child.remove()",
"row().child().remove()"],function(){bb(this);return this});p("row().child.isShown()",function(){var a=this.context;return a.length&&this.length?a[0].aoData[this[0]]._detailsShow||!1:!1});var dc=/^(.+):(name|visIdx|visible)$/,Wb=function(a,b,c,d,e){for(var c=[],d=0,f=e.length;d<f;d++)c.push(B(a,e[d],b));return c};p("columns()",function(a,b){a===k?a="":h.isPlainObject(a)&&(b=a,a="");var b=$a(b),c=this.iterator("table",function(c){var e=a,f=b,g=c.aoColumns,i=D(g,"sName"),j=D(g,"nTh");return Za("column",
e,function(a){var b=Pb(a);if(a==="")return W(g.length);if(b!==null)return[b>=0?b:g.length+b];if(typeof a==="function"){var e=Da(c,f);return h.map(g,function(b,f){return a(f,Wb(c,f,0,0,e),j[f])?f:null})}var k=typeof a==="string"?a.match(dc):"";if(k)switch(k[2]){case "visIdx":case "visible":b=parseInt(k[1],10);if(b<0){var m=h.map(g,function(a,b){return a.bVisible?b:null});return[m[m.length+b]]}return[$(c,b)];case "name":return h.map(i,function(a,b){return a===k[1]?b:null})}else return h(j).filter(a).map(function(){return h.inArray(this,
j)}).toArray()},c,f)},1);c.selector.cols=a;c.selector.opts=b;return c});s("columns().header()","column().header()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].nTh},1)});s("columns().footer()","column().footer()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].nTf},1)});s("columns().data()","column().data()",function(){return this.iterator("column-rows",Wb,1)});s("columns().dataSrc()","column().dataSrc()",function(){return this.iterator("column",
function(a,b){return a.aoColumns[b].mData},1)});s("columns().cache()","column().cache()",function(a){return this.iterator("column-rows",function(b,c,d,e,f){return ja(b.aoData,f,"search"===a?"_aFilterData":"_aSortData",c)},1)});s("columns().nodes()","column().nodes()",function(){return this.iterator("column-rows",function(a,b,c,d,e){return ja(a.aoData,e,"anCells",b)},1)});s("columns().visible()","column().visible()",function(a,b){return this.iterator("column",function(c,d){if(a===k)return c.aoColumns[d].bVisible;
var e=c.aoColumns,f=e[d],g=c.aoData,i,j,m;if(a!==k&&f.bVisible!==a){if(a){var l=h.inArray(!0,D(e,"bVisible"),d+1);i=0;for(j=g.length;i<j;i++)m=g[i].nTr,e=g[i].anCells,m&&m.insertBefore(e[d],e[l]||null)}else h(D(c.aoData,"anCells",d)).detach();f.bVisible=a;ga(c,c.aoHeader);ga(c,c.aoFooter);if(b===k||b)Y(c),(c.oScroll.sX||c.oScroll.sY)&&Z(c);w(c,null,"column-visibility",[c,d,a]);ya(c)}})});s("columns().indexes()","column().index()",function(a){return this.iterator("column",function(b,c){return"visible"===
a?ba(b,c):c},1)});p("columns.adjust()",function(){return this.iterator("table",function(a){Y(a)},1)});p("column.index()",function(a,b){if(0!==this.context.length){var c=this.context[0];if("fromVisible"===a||"toData"===a)return $(c,b);if("fromData"===a||"toVisible"===a)return ba(c,b)}});p("column()",function(a,b){return ab(this.columns(a,b))});p("cells()",function(a,b,c){h.isPlainObject(a)&&(a.row===k?(c=a,a=null):(c=b,b=null));h.isPlainObject(b)&&(c=b,b=null);if(null===b||b===k)return this.iterator("table",
function(b){var d=a,e=$a(c),f=b.aoData,g=Da(b,e),i=Sb(ja(f,g,"anCells")),j=h([].concat.apply([],i)),l,m=b.aoColumns.length,n,p,t,s,u,v;return Za("cell",d,function(a){var c=typeof a==="function";if(a===null||a===k||c){n=[];p=0;for(t=g.length;p<t;p++){l=g[p];for(s=0;s<m;s++){u={row:l,column:s};if(c){v=f[l];a(u,B(b,l,s),v.anCells?v.anCells[s]:null)&&n.push(u)}else n.push(u)}}return n}return h.isPlainObject(a)?[a]:j.filter(a).map(function(a,b){if(b.parentNode)l=b.parentNode._DT_RowIndex;else{a=0;for(t=
f.length;a<t;a++)if(h.inArray(b,f[a].anCells)!==-1){l=a;break}}return{row:l,column:h.inArray(b,f[l].anCells)}}).toArray()},b,e)});var d=this.columns(b,c),e=this.rows(a,c),f,g,i,j,m,l=this.iterator("table",function(a,b){f=[];g=0;for(i=e[b].length;g<i;g++){j=0;for(m=d[b].length;j<m;j++)f.push({row:e[b][g],column:d[b][j]})}return f},1);h.extend(l.selector,{cols:b,rows:a,opts:c});return l});s("cells().nodes()","cell().node()",function(){return this.iterator("cell",function(a,b,c){return(a=a.aoData[b].anCells)?
a[c]:k},1)});p("cells().data()",function(){return this.iterator("cell",function(a,b,c){return B(a,b,c)},1)});s("cells().cache()","cell().cache()",function(a){a="search"===a?"_aFilterData":"_aSortData";return this.iterator("cell",function(b,c,d){return b.aoData[c][a][d]},1)});s("cells().render()","cell().render()",function(a){return this.iterator("cell",function(b,c,d){return B(b,c,d,a)},1)});s("cells().indexes()","cell().index()",function(){return this.iterator("cell",function(a,b,c){return{row:b,
column:c,columnVisible:ba(a,c)}},1)});s("cells().invalidate()","cell().invalidate()",function(a){return this.iterator("cell",function(b,c,d){ea(b,c,a,d)})});p("cell()",function(a,b,c){return ab(this.cells(a,b,c))});p("cell().data()",function(a){var b=this.context,c=this[0];if(a===k)return b.length&&c.length?B(b[0],c[0].row,c[0].column):k;ib(b[0],c[0].row,c[0].column,a);ea(b[0],c[0].row,"data",c[0].column);return this});p("order()",function(a,b){var c=this.context;if(a===k)return 0!==c.length?c[0].aaSorting:
k;"number"===typeof a?a=[[a,b]]:h.isArray(a[0])||(a=Array.prototype.slice.call(arguments));return this.iterator("table",function(b){b.aaSorting=a.slice()})});p("order.listener()",function(a,b,c){return this.iterator("table",function(d){Oa(d,a,b,c)})});p(["columns().order()","column().order()"],function(a){var b=this;return this.iterator("table",function(c,d){var e=[];h.each(b[d],function(b,c){e.push([c,a])});c.aaSorting=e})});p("search()",function(a,b,c,d){var e=this.context;return a===k?0!==e.length?
e[0].oPreviousSearch.sSearch:k:this.iterator("table",function(e){e.oFeatures.bFilter&&ha(e,h.extend({},e.oPreviousSearch,{sSearch:a+"",bRegex:null===b?!1:b,bSmart:null===c?!0:c,bCaseInsensitive:null===d?!0:d}),1)})});s("columns().search()","column().search()",function(a,b,c,d){return this.iterator("column",function(e,f){var g=e.aoPreSearchCols;if(a===k)return g[f].sSearch;e.oFeatures.bFilter&&(h.extend(g[f],{sSearch:a+"",bRegex:null===b?!1:b,bSmart:null===c?!0:c,bCaseInsensitive:null===d?!0:d}),ha(e,
e.oPreviousSearch,1))})});p("state()",function(){return this.context.length?this.context[0].oSavedState:null});p("state.clear()",function(){return this.iterator("table",function(a){a.fnStateSaveCallback.call(a.oInstance,a,{})})});p("state.loaded()",function(){return this.context.length?this.context[0].oLoadedState:null});p("state.save()",function(){return this.iterator("table",function(a){ya(a)})});m.versionCheck=m.fnVersionCheck=function(a){for(var b=m.version.split("."),a=a.split("."),c,d,e=0,f=
a.length;e<f;e++)if(c=parseInt(b[e],10)||0,d=parseInt(a[e],10)||0,c!==d)return c>d;return!0};m.isDataTable=m.fnIsDataTable=function(a){var b=h(a).get(0),c=!1;h.each(m.settings,function(a,e){var f=e.nScrollHead?h("table",e.nScrollHead)[0]:null,g=e.nScrollFoot?h("table",e.nScrollFoot)[0]:null;if(e.nTable===b||f===b||g===b)c=!0});return c};m.tables=m.fnTables=function(a){var b=!1;h.isPlainObject(a)&&(b=a.api,a=a.visible);var c=h.map(m.settings,function(b){if(!a||a&&h(b.nTable).is(":visible"))return b.nTable});
return b?new t(c):c};m.util={throttle:ua,escapeRegex:va};m.camelToHungarian=I;p("$()",function(a,b){var c=this.rows(b).nodes(),c=h(c);return h([].concat(c.filter(a).toArray(),c.find(a).toArray()))});h.each(["on","one","off"],function(a,b){p(b+"()",function(){var a=Array.prototype.slice.call(arguments);a[0].match(/\.dt\b/)||(a[0]+=".dt");var d=h(this.tables().nodes());d[b].apply(d,a);return this})});p("clear()",function(){return this.iterator("table",function(a){na(a)})});p("settings()",function(){return new t(this.context,
this.context)});p("init()",function(){var a=this.context;return a.length?a[0].oInit:null});p("data()",function(){return this.iterator("table",function(a){return D(a.aoData,"_aData")}).flatten()});p("destroy()",function(a){a=a||!1;return this.iterator("table",function(b){var c=b.nTableWrapper.parentNode,d=b.oClasses,e=b.nTable,f=b.nTBody,g=b.nTHead,i=b.nTFoot,j=h(e),f=h(f),k=h(b.nTableWrapper),l=h.map(b.aoData,function(a){return a.nTr}),p;b.bDestroying=!0;w(b,"aoDestroyCallback","destroy",[b]);a||
(new t(b)).columns().visible(!0);k.unbind(".DT").find(":not(tbody *)").unbind(".DT");h(Fa).unbind(".DT-"+b.sInstance);e!=g.parentNode&&(j.children("thead").detach(),j.append(g));i&&e!=i.parentNode&&(j.children("tfoot").detach(),j.append(i));b.aaSorting=[];b.aaSortingFixed=[];xa(b);h(l).removeClass(b.asStripeClasses.join(" "));h("th, td",g).removeClass(d.sSortable+" "+d.sSortableAsc+" "+d.sSortableDesc+" "+d.sSortableNone);b.bJUI&&(h("th span."+d.sSortIcon+", td span."+d.sSortIcon,g).detach(),h("th, td",
g).each(function(){var a=h("div."+d.sSortJUIWrapper,this);h(this).append(a.contents());a.detach()}));f.children().detach();f.append(l);g=a?"remove":"detach";j[g]();k[g]();!a&&c&&(c.insertBefore(e,b.nTableReinsertBefore),j.css("width",b.sDestroyWidth).removeClass(d.sTable),(p=b.asDestroyStripes.length)&&f.children().each(function(a){h(this).addClass(b.asDestroyStripes[a%p])}));c=h.inArray(b,m.settings);-1!==c&&m.settings.splice(c,1)})});h.each(["column","row","cell"],function(a,b){p(b+"s().every()",
function(a){return this.iterator(b,function(d,e,f,g,h){a.call((new t(d))[b](e,"cell"===b?f:k),e,f,g,h)})})});p("i18n()",function(a,b,c){var d=this.context[0],a=P(a)(d.oLanguage);a===k&&(a=b);c!==k&&h.isPlainObject(a)&&(a=a[c]!==k?a[c]:a._);return a.replace("%d",c)});m.version="1.10.9";m.settings=[];m.models={};m.models.oSearch={bCaseInsensitive:!0,sSearch:"",bRegex:!1,bSmart:!0};m.models.oRow={nTr:null,anCells:null,_aData:[],_aSortData:null,_aFilterData:null,_sFilterRow:null,_sRowStripe:"",src:null,
idx:-1};m.models.oColumn={idx:null,aDataSort:null,asSorting:null,bSearchable:null,bSortable:null,bVisible:null,_sManualType:null,_bAttrSrc:!1,fnCreatedCell:null,fnGetData:null,fnSetData:null,mData:null,mRender:null,nTh:null,nTf:null,sClass:null,sContentPadding:null,sDefaultContent:null,sName:null,sSortDataType:"std",sSortingClass:null,sSortingClassJUI:null,sTitle:null,sType:null,sWidth:null,sWidthOrig:null};m.defaults={aaData:null,aaSorting:[[0,"asc"]],aaSortingFixed:[],ajax:null,aLengthMenu:[10,
25,50,100],aoColumns:null,aoColumnDefs:null,aoSearchCols:[],asStripeClasses:null,bAutoWidth:!0,bDeferRender:!1,bDestroy:!1,bFilter:!0,bInfo:!0,bJQueryUI:!1,bLengthChange:!0,bPaginate:!0,bProcessing:!1,bRetrieve:!1,bScrollCollapse:!1,bServerSide:!1,bSort:!0,bSortMulti:!0,bSortCellsTop:!1,bSortClasses:!0,bStateSave:!1,fnCreatedRow:null,fnDrawCallback:null,fnFooterCallback:null,fnFormatNumber:function(a){return a.toString().replace(/\B(?=(\d{3})+(?!\d))/g,this.oLanguage.sThousands)},fnHeaderCallback:null,
fnInfoCallback:null,fnInitComplete:null,fnPreDrawCallback:null,fnRowCallback:null,fnServerData:null,fnServerParams:null,fnStateLoadCallback:function(a){try{return JSON.parse((-1===a.iStateDuration?sessionStorage:localStorage).getItem("DataTables_"+a.sInstance+"_"+location.pathname))}catch(b){}},fnStateLoadParams:null,fnStateLoaded:null,fnStateSaveCallback:function(a,b){try{(-1===a.iStateDuration?sessionStorage:localStorage).setItem("DataTables_"+a.sInstance+"_"+location.pathname,JSON.stringify(b))}catch(c){}},
fnStateSaveParams:null,iStateDuration:7200,iDeferLoading:null,iDisplayLength:10,iDisplayStart:0,iTabIndex:0,oClasses:{},oLanguage:{oAria:{sSortAscending:": activate to sort column ascending",sSortDescending:": activate to sort column descending"},oPaginate:{sFirst:"First",sLast:"Last",sNext:"Next",sPrevious:"Previous"},sEmptyTable:"No data available in table",sInfo:"Showing _START_ to _END_ of _TOTAL_ entries",sInfoEmpty:"Showing 0 to 0 of 0 entries",sInfoFiltered:"(filtered from _MAX_ total entries)",
sInfoPostFix:"",sDecimal:"",sThousands:",",sLengthMenu:"Show _MENU_ entries",sLoadingRecords:"Loading...",sProcessing:"Processing...",sSearch:"Search:",sSearchPlaceholder:"",sUrl:"",sZeroRecords:"No matching records found"},oSearch:h.extend({},m.models.oSearch),sAjaxDataProp:"data",sAjaxSource:null,sDom:"lfrtip",searchDelay:null,sPaginationType:"simple_numbers",sScrollX:"",sScrollXInner:"",sScrollY:"",sServerMethod:"GET",renderer:null,rowId:"DT_RowId"};X(m.defaults);m.defaults.column={aDataSort:null,
iDataSort:-1,asSorting:["asc","desc"],bSearchable:!0,bSortable:!0,bVisible:!0,fnCreatedCell:null,mData:null,mRender:null,sCellType:"td",sClass:"",sContentPadding:"",sDefaultContent:null,sName:"",sSortDataType:"std",sTitle:null,sType:null,sWidth:null};X(m.defaults.column);m.models.oSettings={oFeatures:{bAutoWidth:null,bDeferRender:null,bFilter:null,bInfo:null,bLengthChange:null,bPaginate:null,bProcessing:null,bServerSide:null,bSort:null,bSortMulti:null,bSortClasses:null,bStateSave:null},oScroll:{bCollapse:null,
iBarWidth:0,sX:null,sXInner:null,sY:null},oLanguage:{fnInfoCallback:null},oBrowser:{bScrollOversize:!1,bScrollbarLeft:!1,bBounding:!1,barWidth:0},ajax:null,aanFeatures:[],aoData:[],aiDisplay:[],aiDisplayMaster:[],aIds:{},aoColumns:[],aoHeader:[],aoFooter:[],oPreviousSearch:{},aoPreSearchCols:[],aaSorting:null,aaSortingFixed:[],asStripeClasses:null,asDestroyStripes:[],sDestroyWidth:0,aoRowCallback:[],aoHeaderCallback:[],aoFooterCallback:[],aoDrawCallback:[],aoRowCreatedCallback:[],aoPreDrawCallback:[],
aoInitComplete:[],aoStateSaveParams:[],aoStateLoadParams:[],aoStateLoaded:[],sTableId:"",nTable:null,nTHead:null,nTFoot:null,nTBody:null,nTableWrapper:null,bDeferLoading:!1,bInitialised:!1,aoOpenRows:[],sDom:null,searchDelay:null,sPaginationType:"two_button",iStateDuration:0,aoStateSave:[],aoStateLoad:[],oSavedState:null,oLoadedState:null,sAjaxSource:null,sAjaxDataProp:null,bAjaxDataGet:!0,jqXHR:null,json:k,oAjaxData:k,fnServerData:null,aoServerParams:[],sServerMethod:null,fnFormatNumber:null,aLengthMenu:null,
iDraw:0,bDrawing:!1,iDrawError:-1,_iDisplayLength:10,_iDisplayStart:0,_iRecordsTotal:0,_iRecordsDisplay:0,bJUI:null,oClasses:{},bFiltered:!1,bSorted:!1,bSortCellsTop:null,oInit:null,aoDestroyCallback:[],fnRecordsTotal:function(){return"ssp"==y(this)?1*this._iRecordsTotal:this.aiDisplayMaster.length},fnRecordsDisplay:function(){return"ssp"==y(this)?1*this._iRecordsDisplay:this.aiDisplay.length},fnDisplayEnd:function(){var a=this._iDisplayLength,b=this._iDisplayStart,c=b+a,d=this.aiDisplay.length,e=
this.oFeatures,f=e.bPaginate;return e.bServerSide?!1===f||-1===a?b+d:Math.min(b+a,this._iRecordsDisplay):!f||c>d||-1===a?d:c},oInstance:null,sInstance:null,iTabIndex:0,nScrollHead:null,nScrollFoot:null,aLastSort:[],oPlugins:{},rowIdFn:null,rowId:null};m.ext=v={buttons:{},classes:{},errMode:"alert",feature:[],search:[],selector:{cell:[],column:[],row:[]},internal:{},legacy:{ajax:null},pager:{},renderer:{pageButton:{},header:{}},order:{},type:{detect:[],search:{},order:{}},_unique:0,fnVersionCheck:m.fnVersionCheck,
iApiIndex:0,oJUIClasses:{},sVersion:m.version};h.extend(v,{afnFiltering:v.search,aTypes:v.type.detect,ofnSearch:v.type.search,oSort:v.type.order,afnSortData:v.order,aoFeatures:v.feature,oApi:v.internal,oStdClasses:v.classes,oPagination:v.pager});h.extend(m.ext.classes,{sTable:"dataTable",sNoFooter:"no-footer",sPageButton:"paginate_button",sPageButtonActive:"current",sPageButtonDisabled:"disabled",sStripeOdd:"odd",sStripeEven:"even",sRowEmpty:"dataTables_empty",sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter",
sInfo:"dataTables_info",sPaging:"dataTables_paginate paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"sorting_asc",sSortDesc:"sorting_desc",sSortable:"sorting",sSortableAsc:"sorting_asc_disabled",sSortableDesc:"sorting_desc_disabled",sSortableNone:"sorting_disabled",sSortColumn:"sorting_",sFilterInput:"",sLengthSelect:"",sScrollWrapper:"dataTables_scroll",sScrollHead:"dataTables_scrollHead",sScrollHeadInner:"dataTables_scrollHeadInner",sScrollBody:"dataTables_scrollBody",
sScrollFoot:"dataTables_scrollFoot",sScrollFootInner:"dataTables_scrollFootInner",sHeaderTH:"",sFooterTH:"",sSortJUIAsc:"",sSortJUIDesc:"",sSortJUI:"",sSortJUIAscAllowed:"",sSortJUIDescAllowed:"",sSortJUIWrapper:"",sSortIcon:"",sJUIHeader:"",sJUIFooter:""});var Ea="",Ea="",G=Ea+"ui-state-default",ka=Ea+"css_right ui-icon ui-icon-",Xb=Ea+"fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix";h.extend(m.ext.oJUIClasses,m.ext.classes,{sPageButton:"fg-button ui-button "+G,sPageButtonActive:"ui-state-disabled",
sPageButtonDisabled:"ui-state-disabled",sPaging:"dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi ui-buttonset-multi paging_",sSortAsc:G+" sorting_asc",sSortDesc:G+" sorting_desc",sSortable:G+" sorting",sSortableAsc:G+" sorting_asc_disabled",sSortableDesc:G+" sorting_desc_disabled",sSortableNone:G+" sorting_disabled",sSortJUIAsc:ka+"triangle-1-n",sSortJUIDesc:ka+"triangle-1-s",sSortJUI:ka+"carat-2-n-s",sSortJUIAscAllowed:ka+"carat-1-n",sSortJUIDescAllowed:ka+"carat-1-s",sSortJUIWrapper:"DataTables_sort_wrapper",
sSortIcon:"DataTables_sort_icon",sScrollHead:"dataTables_scrollHead "+G,sScrollFoot:"dataTables_scrollFoot "+G,sHeaderTH:G,sFooterTH:G,sJUIHeader:Xb+" ui-corner-tl ui-corner-tr",sJUIFooter:Xb+" ui-corner-bl ui-corner-br"});var Mb=m.ext.pager;h.extend(Mb,{simple:function(){return["previous","next"]},full:function(){return["first","previous","next","last"]},numbers:function(a,b){return[Aa(a,b)]},simple_numbers:function(a,b){return["previous",Aa(a,b),"next"]},full_numbers:function(a,b){return["first",
"previous",Aa(a,b),"next","last"]},_numbers:Aa,numbers_length:7});h.extend(!0,m.ext.renderer,{pageButton:{_:function(a,b,c,d,e,f){var g=a.oClasses,i=a.oLanguage.oPaginate,j,k,l=0,m=function(b,d){var p,q,t,s,u=function(b){Ta(a,b.data.action,true)};p=0;for(q=d.length;p<q;p++){s=d[p];if(h.isArray(s)){t=h("<"+(s.DT_el||"div")+"/>").appendTo(b);m(t,s)}else{j=null;k="";switch(s){case "ellipsis":b.append('<span class="ellipsis">&#x2026;</span>');break;case "first":j=i.sFirst;k=s+(e>0?"":" "+g.sPageButtonDisabled);
break;case "previous":j=i.sPrevious;k=s+(e>0?"":" "+g.sPageButtonDisabled);break;case "next":j=i.sNext;k=s+(e<f-1?"":" "+g.sPageButtonDisabled);break;case "last":j=i.sLast;k=s+(e<f-1?"":" "+g.sPageButtonDisabled);break;default:j=s+1;k=e===s?g.sPageButtonActive:""}if(j!==null){t=h("<a>",{"class":g.sPageButton+" "+k,"aria-controls":a.sTableId,"data-dt-idx":l,tabindex:a.iTabIndex,id:c===0&&typeof s==="string"?a.sTableId+"_"+s:null}).html(j).appendTo(b);Va(t,{action:s},u);l++}}}},p;try{p=h(b).find(T.activeElement).data("dt-idx")}catch(t){}m(h(b).empty(),
d);p&&h(b).find("[data-dt-idx="+p+"]").focus()}}});h.extend(m.ext.type.detect,[function(a,b){var c=b.oLanguage.sDecimal;return Ya(a,c)?"num"+c:null},function(a){if(a&&!(a instanceof Date)&&(!ac.test(a)||!bc.test(a)))return null;var b=Date.parse(a);return null!==b&&!isNaN(b)||K(a)?"date":null},function(a,b){var c=b.oLanguage.sDecimal;return Ya(a,c,!0)?"num-fmt"+c:null},function(a,b){var c=b.oLanguage.sDecimal;return Rb(a,c)?"html-num"+c:null},function(a,b){var c=b.oLanguage.sDecimal;return Rb(a,c,
!0)?"html-num-fmt"+c:null},function(a){return K(a)||"string"===typeof a&&-1!==a.indexOf("<")?"html":null}]);h.extend(m.ext.type.search,{html:function(a){return K(a)?a:"string"===typeof a?a.replace(Ob," ").replace(Ca,""):""},string:function(a){return K(a)?a:"string"===typeof a?a.replace(Ob," "):a}});var Ba=function(a,b,c,d){if(0!==a&&(!a||"-"===a))return-Infinity;b&&(a=Qb(a,b));a.replace&&(c&&(a=a.replace(c,"")),d&&(a=a.replace(d,"")));return 1*a};h.extend(v.type.order,{"date-pre":function(a){return Date.parse(a)||
0},"html-pre":function(a){return K(a)?"":a.replace?a.replace(/<.*?>/g,"").toLowerCase():a+""},"string-pre":function(a){return K(a)?"":"string"===typeof a?a.toLowerCase():!a.toString?"":a.toString()},"string-asc":function(a,b){return a<b?-1:a>b?1:0},"string-desc":function(a,b){return a<b?1:a>b?-1:0}});cb("");h.extend(!0,m.ext.renderer,{header:{_:function(a,b,c,d){h(a.nTable).on("order.dt.DT",function(e,f,g,h){if(a===f){e=c.idx;b.removeClass(c.sSortingClass+" "+d.sSortAsc+" "+d.sSortDesc).addClass(h[e]==
"asc"?d.sSortAsc:h[e]=="desc"?d.sSortDesc:c.sSortingClass)}})},jqueryui:function(a,b,c,d){h("<div/>").addClass(d.sSortJUIWrapper).append(b.contents()).append(h("<span/>").addClass(d.sSortIcon+" "+c.sSortingClassJUI)).appendTo(b);h(a.nTable).on("order.dt.DT",function(e,f,g,h){if(a===f){e=c.idx;b.removeClass(d.sSortAsc+" "+d.sSortDesc).addClass(h[e]=="asc"?d.sSortAsc:h[e]=="desc"?d.sSortDesc:c.sSortingClass);b.find("span."+d.sSortIcon).removeClass(d.sSortJUIAsc+" "+d.sSortJUIDesc+" "+d.sSortJUI+" "+
d.sSortJUIAscAllowed+" "+d.sSortJUIDescAllowed).addClass(h[e]=="asc"?d.sSortJUIAsc:h[e]=="desc"?d.sSortJUIDesc:c.sSortingClassJUI)}})}}});m.render={number:function(a,b,c,d,e){return{display:function(f){if("number"!==typeof f&&"string"!==typeof f)return f;var g=0>f?"-":"",f=Math.abs(parseFloat(f)),h=parseInt(f,10),f=c?b+(f-h).toFixed(c).substring(2):"";return g+(d||"")+h.toString().replace(/\B(?=(\d{3})+(?!\d))/g,a)+f+(e||"")}}}};h.extend(m.ext.internal,{_fnExternApiFunc:Nb,_fnBuildAjax:ra,_fnAjaxUpdate:lb,
_fnAjaxParameters:ub,_fnAjaxUpdateDraw:vb,_fnAjaxDataSrc:sa,_fnAddColumn:Ga,_fnColumnOptions:la,_fnAdjustColumnSizing:Y,_fnVisibleToColumnIndex:$,_fnColumnIndexToVisible:ba,_fnVisbleColumns:ca,_fnGetColumns:aa,_fnColumnTypes:Ia,_fnApplyColumnDefs:hb,_fnHungarianMap:X,_fnCamelToHungarian:I,_fnLanguageCompat:S,_fnBrowserDetect:fb,_fnAddData:L,_fnAddTr:ma,_fnNodeToDataIndex:function(a,b){return b._DT_RowIndex!==k?b._DT_RowIndex:null},_fnNodeToColumnIndex:function(a,b,c){return h.inArray(c,a.aoData[b].anCells)},
_fnGetCellData:B,_fnSetCellData:ib,_fnSplitObjNotation:La,_fnGetObjectDataFn:P,_fnSetObjectDataFn:Q,_fnGetDataMaster:Ma,_fnClearTable:na,_fnDeleteIndex:oa,_fnInvalidate:ea,_fnGetRowElements:Ka,_fnCreateTr:Ja,_fnBuildHead:kb,_fnDrawHead:ga,_fnDraw:M,_fnReDraw:R,_fnAddOptionsHtml:nb,_fnDetectHeader:fa,_fnGetUniqueThs:qa,_fnFeatureHtmlFilter:pb,_fnFilterComplete:ha,_fnFilterCustom:yb,_fnFilterColumn:xb,_fnFilter:wb,_fnFilterCreateSearch:Qa,_fnEscapeRegex:va,_fnFilterData:zb,_fnFeatureHtmlInfo:sb,_fnUpdateInfo:Cb,
_fnInfoMacros:Db,_fnInitialise:ia,_fnInitComplete:ta,_fnLengthChange:Ra,_fnFeatureHtmlLength:ob,_fnFeatureHtmlPaginate:tb,_fnPageChange:Ta,_fnFeatureHtmlProcessing:qb,_fnProcessingDisplay:C,_fnFeatureHtmlTable:rb,_fnScrollDraw:Z,_fnApplyToChildren:H,_fnCalculateColumnWidths:Ha,_fnThrottle:ua,_fnConvertToWidth:Fb,_fnGetWidestNode:Gb,_fnGetMaxLenString:Hb,_fnStringToCss:u,_fnSortFlatten:V,_fnSort:mb,_fnSortAria:Jb,_fnSortListener:Ua,_fnSortAttachListener:Oa,_fnSortingClasses:xa,_fnSortData:Ib,_fnSaveState:ya,
_fnLoadState:Kb,_fnSettingsFromNode:za,_fnLog:J,_fnMap:F,_fnBindAction:Va,_fnCallbackReg:z,_fnCallbackFire:w,_fnLengthOverflow:Sa,_fnRenderer:Pa,_fnDataSource:y,_fnRowAttributes:Na,_fnCalculateEnd:function(){}});h.fn.dataTable=m;h.fn.dataTableSettings=m.settings;h.fn.dataTableExt=m.ext;h.fn.DataTable=function(a){return h(this).dataTable(a).api()};h.each(m,function(a,b){h.fn.DataTable[a]=b});return h.fn.dataTable};"function"===typeof define&&define.amd?define("datatables",["jquery"],S):"object"===
typeof exports?module.exports=S(require("jquery")):jQuery&&!jQuery.fn.dataTable&&S(jQuery)})(window,document);

15129
externals/jquery.datatables.js vendored Normal file

File diff suppressed because it is too large Load Diff

5
externals/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

544
externals/jquery.typeahead.css vendored Normal file
View File

@@ -0,0 +1,544 @@
.typeahead__container {
/**
* Restore the font weight unset by the previous rule.
*/
/**
* Show the overflow in IE.
* 1. Show the overflow in Edge.
*/
/**
* Remove the inheritance of text transform in Edge, Firefox, and IE.
* 1. Remove the inheritance of text transform in Firefox.
*/
/**
* 1. Prevent a WebKit bug where (2) destroys native `audio` and `video`
* controls in Android 4.
* 2. Correct the inability to style clickable types in iOS and Safari.
*/
/**
* Remove the inner border and padding in Firefox.
*/
/**
* Restore the focus styles unset by the previous rule.
*/
/**
* Change the border, margin, and padding in all browsers (opinionated).
*/
/**
* 1. Correct the text wrapping in Edge and IE.
* 2. Correct the color inheritance from `fieldset` elements in IE.
* 3. Remove the padding so developers are not caught out when they zero out
* `fieldset` elements in all browsers.
*/
/**
* Remove the default vertical scrollbar in IE.
*/
/**
* 1. Add the correct box sizing in IE 10-.
* 2. Remove the padding in IE 10-.
*/
/**
* Correct the cursor style of increment and decrement buttons in Chrome.
*/
/**
* 1. Correct the odd appearance in Chrome and Safari.
* 2. Correct the outline style in Safari.
*/
/**
* Remove the inner padding and cancel buttons in Chrome and Safari on OS X.
*/
/**
* Correct the text style of placeholders in Chrome, Edge, and Safari.
*/
/**
* 1. Correct the inability to style clickable types in iOS and Safari.
* 2. Change font properties to `inherit` in Safari.
*/
}
.typeahead__container button,
.typeahead__container input,
.typeahead__container optgroup,
.typeahead__container select,
.typeahead__container textarea {
font: inherit;
/* 1 */
margin: 0;
/* 2 */
}
.typeahead__container optgroup {
font-weight: bold;
}
.typeahead__container button,
.typeahead__container input {
/* 1 */
overflow: visible;
}
.typeahead__container button,
.typeahead__container select {
/* 1 */
text-transform: none;
}
.typeahead__container button,
.typeahead__container html [type="button"],
.typeahead__container [type="reset"],
.typeahead__container [type="submit"] {
-webkit-appearance: button;
/* 2 */
}
.typeahead__container button::-moz-focus-inner,
.typeahead__container [type="button"]::-moz-focus-inner,
.typeahead__container [type="reset"]::-moz-focus-inner,
.typeahead__container [type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
.typeahead__container button:-moz-focusring,
.typeahead__container [type="button"]:-moz-focusring,
.typeahead__container [type="reset"]:-moz-focusring,
.typeahead__container [type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
.typeahead__container fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
.typeahead__container legend {
box-sizing: border-box;
/* 1 */
color: inherit;
/* 2 */
display: table;
/* 1 */
max-width: 100%;
/* 1 */
padding: 0;
/* 3 */
white-space: normal;
/* 1 */
}
.typeahead__container textarea {
overflow: auto;
}
.typeahead__container [type="checkbox"],
.typeahead__container [type="radio"] {
box-sizing: border-box;
/* 1 */
padding: 0;
/* 2 */
}
.typeahead__container [type="number"]::-webkit-inner-spin-button,
.typeahead__container [type="number"]::-webkit-outer-spin-button {
height: auto;
}
.typeahead__container [type="search"] {
-webkit-appearance: textfield;
/* 1 */
outline-offset: -2px;
/* 2 */
}
.typeahead__container [type="search"]::-webkit-search-cancel-button,
.typeahead__container [type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
.typeahead__container ::-webkit-input-placeholder {
color: inherit;
opacity: 0.54;
}
.typeahead__container ::-webkit-file-upload-button {
-webkit-appearance: button;
/* 1 */
font: inherit;
/* 2 */
}
.typeahead__container {
position: relative;
font: 14px Lato, "Helvetica Neue", Arial, Helvetica, sans-serif;
}
.typeahead__container * {
box-sizing: border-box;
outline: 0;
}
.typeahead__query {
position: relative;
z-index: 2;
width: 100%;
}
.typeahead__filter {
position: relative;
}
.typeahead__filter button {
min-width: 100%;
white-space: nowrap;
}
.typeahead__filter button:after {
display: inline-block;
margin-left: 4px;
width: 0;
height: 0;
vertical-align: -2px;
content: "";
border: 4px solid;
border-right-color: transparent;
border-bottom-color: transparent;
border-left-color: transparent;
}
.typeahead__field {
font-size: 0;
position: relative;
display: table;
border-collapse: collapse;
width: 100%;
}
.typeahead__field > * {
display: table-cell;
vertical-align: top;
}
.typeahead__query, .typeahead__filter, .typeahead__button {
font-size: 14px;
}
.typeahead__button {
position: relative;
font-size: 0;
width: 1%;
vertical-align: middle;
}
.typeahead__button button {
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
}
.typeahead__field {
color: #555;
}
.typeahead__field input {
display: block;
width: 100%;
height: 32px;
padding: 6px 12px;
background: #fff;
border: 1px solid #ccc;
border-radius: 2px 0 0 2px;
transition: all ease-in-out .15s;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
box-sizing: border-box;
}
.typeahead__field input:focus, .typeahead__field input:active {
border-color: #66afe9;
}
.typeahead__field input[type="search"],
.typeahead__field input[type="search"]::-webkit-search-cancel-button {
-webkit-appearance: none;
appearance: none;
}
.typeahead__field input[type="search"]::-ms-clear {
display: none;
width: 0;
height: 0;
}
.typeahead__container.hint .typeahead__field input {
background: transparent;
}
.typeahead__container.hint .typeahead__field input:last-child, .typeahead__hint {
background: #fff;
}
.typeahead__container button {
display: inline-block;
margin-bottom: 0;
text-align: center;
vertical-align: middle;
-ms-touch-action: manipulation;
touch-action: manipulation;
cursor: pointer;
background-color: #fff;
border: 1px solid #ccc;
height: 32px;
padding: 6px 12px;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
color: #555;
}
.typeahead__container button:hover, .typeahead__container button:focus {
color: #3c3c3c;
background-color: #f5f5f5;
border-color: #b3b3b3;
}
.typeahead__container button:active, .typeahead__container button.active {
background-image: none;
}
.typeahead__container button:focus, .typeahead__container button:active {
border-color: #66afe9;
}
.typeahead__container input.disabled,
.typeahead__container input[disabled],
.typeahead__container button.disabled,
.typeahead__container button[disabled] {
cursor: not-allowed;
pointer-events: none;
opacity: 0.65;
box-shadow: none;
background-color: #fff;
border-color: #ccc;
}
.typeahead__filter, .typeahead__button {
z-index: 1;
}
.typeahead__filter button, .typeahead__button button {
margin-left: -1px;
border-bottom-left-radius: 0;
border-top-left-radius: 0;
}
.typeahead__filter:hover, .typeahead__filter:active, .typeahead__filter:focus, .typeahead__button:hover, .typeahead__button:active, .typeahead__button:focus {
z-index: 1001;
}
.typeahead__filter:hover button:focus, .typeahead__filter:hover button:active, .typeahead__filter:active button:focus, .typeahead__filter:active button:active, .typeahead__filter:focus button:focus, .typeahead__filter:focus button:active, .typeahead__button:hover button:focus, .typeahead__button:hover button:active, .typeahead__button:active button:focus, .typeahead__button:active button:active, .typeahead__button:focus button:focus, .typeahead__button:focus button:active {
z-index: 1001;
}
.typeahead__filter + .typeahead__button button {
margin-left: -2px;
}
.typeahead__container.filter .typeahead__filter {
z-index: 1001;
}
.typeahead__list, .typeahead__dropdown {
position: absolute;
top: 100%;
left: 0;
z-index: 1000;
width: 100%;
min-width: 160px;
padding: 5px 0;
margin: 2px 0 0;
list-style: none;
text-align: left;
background-color: #fff;
border: 1px solid #ccc;
border-radius: 2px;
background-clip: padding-box;
}
.typeahead__result.detached .typeahead__list {
position: relative;
z-index: 1041;
top: initial;
left: initial;
}
.typeahead__dropdown {
right: 0;
left: initial;
z-index: 1001;
}
.typeahead__list > li {
position: relative;
border-top: solid 1px #ccc;
}
.typeahead__list > li:first-child {
border-top: none;
}
.typeahead__list > li > a,
.typeahead__dropdown > li > a {
display: block;
padding: 6px 12px;
clear: both;
color: #333333;
text-decoration: none;
}
.typeahead__list > li > a:hover,
.typeahead__list > li > a:focus,
.typeahead__list > li.active > a,
.typeahead__dropdown > li > a:hover,
.typeahead__dropdown > li > a:focus,
.typeahead__dropdown > li.active > a {
background-color: #f5f5f5;
color: #3c3c3c;
}
.typeahead__list.empty > li > a {
cursor: default;
}
.typeahead__list.empty > li > a:hover,
.typeahead__list.empty > li > a:focus,
.typeahead__list.empty > li.active > a {
background-color: transparent;
}
.typeahead__list > li.typeahead__group {
border-color: #bfdef6;
font-weight: bold;
}
.typeahead__list > li.typeahead__group:first-child {
border-top: solid 1px #bfdef6;
}
.typeahead__list > li.typeahead__group > a,
.typeahead__list > li.typeahead__group > a:hover,
.typeahead__list > li.typeahead__group > a:focus,
.typeahead__list > li.typeahead__group.active > a {
cursor: default;
color: #17639f;
background: #ecf5fc;
}
.typeahead__list > li.typeahead__group + li.typeahead__item {
border-color: #bfdef6;
}
.typeahead__container.result .typeahead__list,
.typeahead__container.filter .typeahead__dropdown,
.typeahead__container.hint .typeahead__hint,
.typeahead__container.backdrop + .typeahead__backdrop {
display: block !important;
}
.typeahead__container .typeahead__list,
.typeahead__container .typeahead__dropdown,
.typeahead__container .typeahead__hint,
.typeahead__container + .typeahead__backdrop {
display: none !important;
}
.typeahead__dropdown li:last-child {
margin-top: 5px;
padding-top: 5px;
border-top: solid 1px #ccc;
}
.typeahead__cancel-button {
visibility: hidden;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
border-radius: 50%;
width: 16px;
height: 16px;
position: absolute;
top: 8px;
right: .8em;
cursor: pointer;
background: url(data:image/svg+xml;charset=utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTYuMC4wLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjwhRE9DVFlQRSBzdmcgUFVCTElDICItLy9XM0MvL0RURCBTVkcgMS4xLy9FTiIgImh0dHA6Ly93d3cudzMub3JnL0dyYXBoaWNzL1NWRy8xLjEvRFREL3N2ZzExLmR0ZCI+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgd2lkdGg9IjE2cHgiIGhlaWdodD0iMTZweCIgdmlld0JveD0iMCAwIDQzOC41MzMgNDM4LjUzMyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgNDM4LjUzMyA0MzguNTMzOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+CjxnPgoJPHBhdGggZD0iTTQwOS4xMzMsMTA5LjIwM2MtMTkuNjA4LTMzLjU5Mi00Ni4yMDUtNjAuMTg5LTc5Ljc5OC03OS43OTZDMjk1LjczNiw5LjgwMSwyNTkuMDU4LDAsMjE5LjI3MywwICAgYy0zOS43ODEsMC03Ni40Nyw5LjgwMS0xMTAuMDYzLDI5LjQwN2MtMzMuNTk1LDE5LjYwNC02MC4xOTIsNDYuMjAxLTc5LjgsNzkuNzk2QzkuODAxLDE0Mi44LDAsMTc5LjQ4OSwwLDIxOS4yNjcgICBjMCwzOS43OCw5LjgwNCw3Ni40NjMsMjkuNDA3LDExMC4wNjJjMTkuNjA3LDMzLjU5Miw0Ni4yMDQsNjAuMTg5LDc5Ljc5OSw3OS43OThjMzMuNTk3LDE5LjYwNSw3MC4yODMsMjkuNDA3LDExMC4wNjMsMjkuNDA3ICAgczc2LjQ3LTkuODAyLDExMC4wNjUtMjkuNDA3YzMzLjU5My0xOS42MDIsNjAuMTg5LTQ2LjIwNiw3OS43OTUtNzkuNzk4YzE5LjYwMy0zMy41OTYsMjkuNDAzLTcwLjI4NCwyOS40MDMtMTEwLjA2MiAgIEM0MzguNTMzLDE3OS40ODUsNDI4LjczMiwxNDIuNzk1LDQwOS4xMzMsMTA5LjIwM3ogTTMyMi42MjEsMjcwLjkzOWMzLjYxNywzLjYxMyw1LjQyOCw3LjkwNSw1LjQyOCwxMi44NTQgICBjMCw1LjEzMy0xLjgxMSw5LjUxNC01LjQyOCwxMy4xMjdsLTI1LjY5MywyNS43MDFjLTMuNjE0LDMuNjEzLTcuOTk0LDUuNDItMTMuMTM1LDUuNDJjLTQuOTQ4LDAtOS4yMzYtMS44MDctMTIuODQ3LTUuNDIgICBsLTUxLjY3Ni01MS42ODJsLTUxLjY3OCw1MS42ODJjLTMuNjE2LDMuNjEzLTcuODk4LDUuNDItMTIuODQ3LDUuNDJjLTUuMTQsMC05LjUxNy0xLjgwNy0xMy4xMzQtNS40MmwtMjUuNjk3LTI1LjcwMSAgIGMtMy42MTYtMy42MTMtNS40MjQtNy45OTQtNS40MjQtMTMuMTI3YzAtNC45NDgsMS44MDktOS4yNCw1LjQyNC0xMi44NTRsNTEuNjc4LTUxLjY3M2wtNTEuNjc4LTUxLjY3OCAgIGMtMy42MTYtMy42MTItNS40MjQtNy44OTgtNS40MjQtMTIuODQ3YzAtNS4xNCwxLjgwOS05LjUxNyw1LjQyNC0xMy4xMzRsMjUuNjk3LTI1LjY5M2MzLjYxNy0zLjYxNiw3Ljk5NC01LjQyNCwxMy4xMzQtNS40MjQgICBjNC45NDksMCw5LjIzMSwxLjgwOSwxMi44NDcsNS40MjRsNTEuNjc4LDUxLjY3NGw1MS42NzYtNTEuNjc0YzMuNjEtMy42MTYsNy44OTgtNS40MjQsMTIuODQ3LTUuNDI0ICAgYzUuMTQxLDAsOS41MjEsMS44MDksMTMuMTM1LDUuNDI0bDI1LjY5MywyNS42OTNjMy42MTcsMy42MTcsNS40MjgsNy45OTQsNS40MjgsMTMuMTM0YzAsNC45NDgtMS44MTEsOS4yMzUtNS40MjgsMTIuODQ3ICAgbC01MS42NzUsNTEuNjc4TDMyMi42MjEsMjcwLjkzOXoiIGZpbGw9IiM1NTU1NTUiLz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8Zz4KPC9nPgo8L3N2Zz4K) no-repeat scroll center center transparent;
}
.typeahead__container.cancel:not(.loading) .typeahead__cancel-button {
visibility: visible;
opacity: .25;
}
.typeahead__container.cancel:not(.loading) .typeahead__cancel-button:hover {
opacity: .4;
}
.typeahead__search-icon {
padding: 0 1.25rem;
width: 16px;
height: 16px;
display: block;
background: url(data:image/svg+xml;charset=utf8;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iaXNvLTg4NTktMSI/Pgo8IS0tIEdlbmVyYXRvcjogQWRvYmUgSWxsdXN0cmF0b3IgMTguMS4xLCBTVkcgRXhwb3J0IFBsdWctSW4gLiBTVkcgVmVyc2lvbjogNi4wMCBCdWlsZCAwKSAgLS0+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0iQ2FwYV8xIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDI1MC4zMTMgMjUwLjMxMyIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMjUwLjMxMyAyNTAuMzEzOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSIgd2lkdGg9IjE2cHgiIGhlaWdodD0iMTZweCI+CjxnIGlkPSJTZWFyY2giPgoJPHBhdGggc3R5bGU9ImZpbGwtcnVsZTpldmVub2RkO2NsaXAtcnVsZTpldmVub2RkOyIgZD0iTTI0NC4xODYsMjE0LjYwNGwtNTQuMzc5LTU0LjM3OGMtMC4yODktMC4yODktMC42MjgtMC40OTEtMC45My0wLjc2ICAgYzEwLjctMTYuMjMxLDE2Ljk0NS0zNS42NiwxNi45NDUtNTYuNTU0QzIwNS44MjIsNDYuMDc1LDE1OS43NDcsMCwxMDIuOTExLDBTMCw0Ni4wNzUsMCwxMDIuOTExICAgYzAsNTYuODM1LDQ2LjA3NCwxMDIuOTExLDEwMi45MSwxMDIuOTExYzIwLjg5NSwwLDQwLjMyMy02LjI0NSw1Ni41NTQtMTYuOTQ1YzAuMjY5LDAuMzAxLDAuNDcsMC42NCwwLjc1OSwwLjkyOWw1NC4zOCw1NC4zOCAgIGM4LjE2OSw4LjE2OCwyMS40MTMsOC4xNjgsMjkuNTgzLDBDMjUyLjM1NCwyMzYuMDE3LDI1Mi4zNTQsMjIyLjc3MywyNDQuMTg2LDIxNC42MDR6IE0xMDIuOTExLDE3MC4xNDYgICBjLTM3LjEzNCwwLTY3LjIzNi0zMC4xMDItNjcuMjM2LTY3LjIzNWMwLTM3LjEzNCwzMC4xMDMtNjcuMjM2LDY3LjIzNi02Ny4yMzZjMzcuMTMyLDAsNjcuMjM1LDMwLjEwMyw2Ny4yMzUsNjcuMjM2ICAgQzE3MC4xNDYsMTQwLjA0NCwxNDAuMDQzLDE3MC4xNDYsMTAyLjkxMSwxNzAuMTQ2eiIgZmlsbD0iIzU1NTU1NSIvPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+CjxnPgo8L2c+Cjwvc3ZnPgo=) no-repeat scroll center center transparent;
}
.typeahead__container.loading .typeahead__query:before, .typeahead__container.loading .typeahead__query:after {
transition: all 0s linear, opacity 0.2s ease;
position: absolute;
z-index: 3;
content: '';
top: 50%;
right: .55em;
margin-top: -10.5px;
width: 21px;
height: 21px;
box-sizing: border-box;
border-radius: 500rem;
border-style: solid;
border-width: .1em;
}
.typeahead__container.loading .typeahead__query:before {
border-color: rgba(0, 0, 0, 0.35);
}
.typeahead__container.loading .typeahead__query:after {
-webkit-animation: button-spin 0.6s linear;
animation: button-spin 0.6s linear;
-webkit-animation-iteration-count: infinite;
animation-iteration-count: infinite;
border-color: #fff transparent transparent;
box-shadow: 0 0 0 1px transparent;
}
@-webkit-keyframes button-spin {
from {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@keyframes button-spin {
from {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}

3083
externals/jquery.typeahead.js vendored Normal file

File diff suppressed because it is too large Load Diff

497
externals/jquery.ui.position.js vendored Normal file
View File

@@ -0,0 +1,497 @@
/*!
* jQuery UI Position v1.10.0
* http://jqueryui.com
*
* Copyright 2013 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*
* http://api.jqueryui.com/position/
*/
(function( $, undefined ) {
$.ui = $.ui || {};
var cachedScrollbarWidth,
max = Math.max,
abs = Math.abs,
round = Math.round,
rhorizontal = /left|center|right/,
rvertical = /top|center|bottom/,
roffset = /[\+\-]\d+%?/,
rposition = /^\w+/,
rpercent = /%$/,
_position = $.fn.position;
function getOffsets( offsets, width, height ) {
return [
parseInt( offsets[ 0 ], 10 ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
parseInt( offsets[ 1 ], 10 ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
];
}
function parseCss( element, property ) {
return parseInt( $.css( element, property ), 10 ) || 0;
}
function getDimensions( elem ) {
var raw = elem[0];
if ( raw.nodeType === 9 ) {
return {
width: elem.width(),
height: elem.height(),
offset: { top: 0, left: 0 }
};
}
if ( $.isWindow( raw ) ) {
return {
width: elem.width(),
height: elem.height(),
offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
};
}
if ( raw.preventDefault ) {
return {
width: 0,
height: 0,
offset: { top: raw.pageY, left: raw.pageX }
};
}
return {
width: elem.outerWidth(),
height: elem.outerHeight(),
offset: elem.offset()
};
}
$.position = {
scrollbarWidth: function() {
if ( cachedScrollbarWidth !== undefined ) {
return cachedScrollbarWidth;
}
var w1, w2,
div = $( "<div style='display:block;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ),
innerDiv = div.children()[0];
$( "body" ).append( div );
w1 = innerDiv.offsetWidth;
div.css( "overflow", "scroll" );
w2 = innerDiv.offsetWidth;
if ( w1 === w2 ) {
w2 = div[0].clientWidth;
}
div.remove();
return (cachedScrollbarWidth = w1 - w2);
},
getScrollInfo: function( within ) {
var overflowX = within.isWindow ? "" : within.element.css( "overflow-x" ),
overflowY = within.isWindow ? "" : within.element.css( "overflow-y" ),
hasOverflowX = overflowX === "scroll" ||
( overflowX === "auto" && within.width < within.element[0].scrollWidth ),
hasOverflowY = overflowY === "scroll" ||
( overflowY === "auto" && within.height < within.element[0].scrollHeight );
return {
width: hasOverflowX ? $.position.scrollbarWidth() : 0,
height: hasOverflowY ? $.position.scrollbarWidth() : 0
};
},
getWithinInfo: function( element ) {
var withinElement = $( element || window ),
isWindow = $.isWindow( withinElement[0] );
return {
element: withinElement,
isWindow: isWindow,
offset: withinElement.offset() || { left: 0, top: 0 },
scrollLeft: withinElement.scrollLeft(),
scrollTop: withinElement.scrollTop(),
width: isWindow ? withinElement.width() : withinElement.outerWidth(),
height: isWindow ? withinElement.height() : withinElement.outerHeight()
};
}
};
$.fn.position = function( options ) {
if ( !options || !options.of ) {
return _position.apply( this, arguments );
}
// make a copy, we don't want to modify arguments
options = $.extend( {}, options );
var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
target = $( options.of ),
within = $.position.getWithinInfo( options.within ),
scrollInfo = $.position.getScrollInfo( within ),
collision = ( options.collision || "flip" ).split( " " ),
offsets = {};
dimensions = getDimensions( target );
if ( target[0].preventDefault ) {
// force left top to allow flipping
options.at = "left top";
}
targetWidth = dimensions.width;
targetHeight = dimensions.height;
targetOffset = dimensions.offset;
// clone to reuse original targetOffset later
basePosition = $.extend( {}, targetOffset );
// force my and at to have valid horizontal and vertical positions
// if a value is missing or invalid, it will be converted to center
$.each( [ "my", "at" ], function() {
var pos = ( options[ this ] || "" ).split( " " ),
horizontalOffset,
verticalOffset;
if ( pos.length === 1) {
pos = rhorizontal.test( pos[ 0 ] ) ?
pos.concat( [ "center" ] ) :
rvertical.test( pos[ 0 ] ) ?
[ "center" ].concat( pos ) :
[ "center", "center" ];
}
pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
// calculate offsets
horizontalOffset = roffset.exec( pos[ 0 ] );
verticalOffset = roffset.exec( pos[ 1 ] );
offsets[ this ] = [
horizontalOffset ? horizontalOffset[ 0 ] : 0,
verticalOffset ? verticalOffset[ 0 ] : 0
];
// reduce to just the positions without the offsets
options[ this ] = [
rposition.exec( pos[ 0 ] )[ 0 ],
rposition.exec( pos[ 1 ] )[ 0 ]
];
});
// normalize collision option
if ( collision.length === 1 ) {
collision[ 1 ] = collision[ 0 ];
}
if ( options.at[ 0 ] === "right" ) {
basePosition.left += targetWidth;
} else if ( options.at[ 0 ] === "center" ) {
basePosition.left += targetWidth / 2;
}
if ( options.at[ 1 ] === "bottom" ) {
basePosition.top += targetHeight;
} else if ( options.at[ 1 ] === "center" ) {
basePosition.top += targetHeight / 2;
}
atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
basePosition.left += atOffset[ 0 ];
basePosition.top += atOffset[ 1 ];
return this.each(function() {
var collisionPosition, using,
elem = $( this ),
elemWidth = elem.outerWidth(),
elemHeight = elem.outerHeight(),
marginLeft = parseCss( this, "marginLeft" ),
marginTop = parseCss( this, "marginTop" ),
collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width,
collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height,
position = $.extend( {}, basePosition ),
myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
if ( options.my[ 0 ] === "right" ) {
position.left -= elemWidth;
} else if ( options.my[ 0 ] === "center" ) {
position.left -= elemWidth / 2;
}
if ( options.my[ 1 ] === "bottom" ) {
position.top -= elemHeight;
} else if ( options.my[ 1 ] === "center" ) {
position.top -= elemHeight / 2;
}
position.left += myOffset[ 0 ];
position.top += myOffset[ 1 ];
// if the browser doesn't support fractions, then round for consistent results
if ( !$.support.offsetFractions ) {
position.left = round( position.left );
position.top = round( position.top );
}
collisionPosition = {
marginLeft: marginLeft,
marginTop: marginTop
};
$.each( [ "left", "top" ], function( i, dir ) {
if ( $.ui.position[ collision[ i ] ] ) {
$.ui.position[ collision[ i ] ][ dir ]( position, {
targetWidth: targetWidth,
targetHeight: targetHeight,
elemWidth: elemWidth,
elemHeight: elemHeight,
collisionPosition: collisionPosition,
collisionWidth: collisionWidth,
collisionHeight: collisionHeight,
offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
my: options.my,
at: options.at,
within: within,
elem : elem
});
}
});
if ( options.using ) {
// adds feedback as second argument to using callback, if present
using = function( props ) {
var left = targetOffset.left - position.left,
right = left + targetWidth - elemWidth,
top = targetOffset.top - position.top,
bottom = top + targetHeight - elemHeight,
feedback = {
target: {
element: target,
left: targetOffset.left,
top: targetOffset.top,
width: targetWidth,
height: targetHeight
},
element: {
element: elem,
left: position.left,
top: position.top,
width: elemWidth,
height: elemHeight
},
horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
};
if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
feedback.horizontal = "center";
}
if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
feedback.vertical = "middle";
}
if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
feedback.important = "horizontal";
} else {
feedback.important = "vertical";
}
options.using.call( this, props, feedback );
};
}
elem.offset( $.extend( position, { using: using } ) );
});
};
$.ui.position = {
fit: {
left: function( position, data ) {
var within = data.within,
withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
outerWidth = within.width,
collisionPosLeft = position.left - data.collisionPosition.marginLeft,
overLeft = withinOffset - collisionPosLeft,
overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
newOverRight;
// element is wider than within
if ( data.collisionWidth > outerWidth ) {
// element is initially over the left side of within
if ( overLeft > 0 && overRight <= 0 ) {
newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset;
position.left += overLeft - newOverRight;
// element is initially over right side of within
} else if ( overRight > 0 && overLeft <= 0 ) {
position.left = withinOffset;
// element is initially over both left and right sides of within
} else {
if ( overLeft > overRight ) {
position.left = withinOffset + outerWidth - data.collisionWidth;
} else {
position.left = withinOffset;
}
}
// too far left -> align with left edge
} else if ( overLeft > 0 ) {
position.left += overLeft;
// too far right -> align with right edge
} else if ( overRight > 0 ) {
position.left -= overRight;
// adjust based on position and margin
} else {
position.left = max( position.left - collisionPosLeft, position.left );
}
},
top: function( position, data ) {
var within = data.within,
withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
outerHeight = data.within.height,
collisionPosTop = position.top - data.collisionPosition.marginTop,
overTop = withinOffset - collisionPosTop,
overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
newOverBottom;
// element is taller than within
if ( data.collisionHeight > outerHeight ) {
// element is initially over the top of within
if ( overTop > 0 && overBottom <= 0 ) {
newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset;
position.top += overTop - newOverBottom;
// element is initially over bottom of within
} else if ( overBottom > 0 && overTop <= 0 ) {
position.top = withinOffset;
// element is initially over both top and bottom of within
} else {
if ( overTop > overBottom ) {
position.top = withinOffset + outerHeight - data.collisionHeight;
} else {
position.top = withinOffset;
}
}
// too far up -> align with top
} else if ( overTop > 0 ) {
position.top += overTop;
// too far down -> align with bottom edge
} else if ( overBottom > 0 ) {
position.top -= overBottom;
// adjust based on position and margin
} else {
position.top = max( position.top - collisionPosTop, position.top );
}
}
},
flip: {
left: function( position, data ) {
var within = data.within,
withinOffset = within.offset.left + within.scrollLeft,
outerWidth = within.width,
offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
collisionPosLeft = position.left - data.collisionPosition.marginLeft,
overLeft = collisionPosLeft - offsetLeft,
overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
myOffset = data.my[ 0 ] === "left" ?
-data.elemWidth :
data.my[ 0 ] === "right" ?
data.elemWidth :
0,
atOffset = data.at[ 0 ] === "left" ?
data.targetWidth :
data.at[ 0 ] === "right" ?
-data.targetWidth :
0,
offset = -2 * data.offset[ 0 ],
newOverRight,
newOverLeft;
if ( overLeft < 0 ) {
newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset;
if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
position.left += myOffset + atOffset + offset;
}
}
else if ( overRight > 0 ) {
newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft;
if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
position.left += myOffset + atOffset + offset;
}
}
},
top: function( position, data ) {
var within = data.within,
withinOffset = within.offset.top + within.scrollTop,
outerHeight = within.height,
offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
collisionPosTop = position.top - data.collisionPosition.marginTop,
overTop = collisionPosTop - offsetTop,
overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
top = data.my[ 1 ] === "top",
myOffset = top ?
-data.elemHeight :
data.my[ 1 ] === "bottom" ?
data.elemHeight :
0,
atOffset = data.at[ 1 ] === "top" ?
data.targetHeight :
data.at[ 1 ] === "bottom" ?
-data.targetHeight :
0,
offset = -2 * data.offset[ 1 ],
newOverTop,
newOverBottom;
if ( overTop < 0 ) {
newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset;
if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) ) {
position.top += myOffset + atOffset + offset;
}
}
else if ( overBottom > 0 ) {
newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop;
if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) {
position.top += myOffset + atOffset + offset;
}
}
}
},
flipfit: {
left: function() {
$.ui.position.flip.left.apply( this, arguments );
$.ui.position.fit.left.apply( this, arguments );
},
top: function() {
$.ui.position.flip.top.apply( this, arguments );
$.ui.position.fit.top.apply( this, arguments );
}
}
};
// fraction support test
(function () {
var testElement, testElementParent, testElementStyle, offsetLeft, i,
body = document.getElementsByTagName( "body" )[ 0 ],
div = document.createElement( "div" );
//Create a "fake body" for testing based on method used in jQuery.support
testElement = document.createElement( body ? "div" : "body" );
testElementStyle = {
visibility: "hidden",
width: 0,
height: 0,
border: 0,
margin: 0,
background: "none"
};
if ( body ) {
$.extend( testElementStyle, {
position: "absolute",
left: "-1000px",
top: "-1000px"
});
}
for ( i in testElementStyle ) {
testElement.style[ i ] = testElementStyle[ i ];
}
testElement.appendChild( div );
testElementParent = body || document.documentElement;
testElementParent.insertBefore( testElement, testElementParent.firstChild );
div.style.cssText = "position: absolute; left: 10.7432222px;";
offsetLeft = $( div ).offset().left;
$.support.offsetFractions = offsetLeft > 10 && offsetLeft < 11;
testElement.innerHTML = "";
testElementParent.removeChild( testElement );
})();
}( jQuery ) );

View File

@@ -3,7 +3,7 @@ const isCI = require("is-ci");
module.exports = {
launch: {
headless: isCI,
slowMo: 55,
slowMo: 30,
defaultViewport: null,
ignoreHTTPSErrors: true,
args: ["--disable-web-security"]

View File

@@ -2082,8 +2082,7 @@ a:link {
.resourceTreeAndTabs {
display: flex;
flex: 1 1 auto;
overflow-x: auto;
overflow-y: hidden;
overflow: auto;
height: 100%;
}

16481
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,26 +6,26 @@
"dependencies": {
"@azure/cosmos": "3.9.0",
"@azure/cosmos-language-service": "0.0.4",
"@jupyterlab/services": "6.0.0-rc.2",
"@jupyterlab/terminal": "3.0.0-rc.2",
"@microsoft/applicationinsights-web": "2.5.9",
"@nteract/commutable": "7.3.2",
"@nteract/connected-components": "6.8.2",
"@nteract/core": "15.1.0",
"@jupyterlab/services": "4.2.0",
"@jupyterlab/terminal": "1.2.1",
"@microsoft/applicationinsights-web": "2.5.8",
"@nteract/commutable": "7.1.4",
"@nteract/connected-components": "6.7.8",
"@nteract/core": "13.0.0",
"@nteract/data-explorer": "8.0.3",
"@nteract/directory-listing": "2.0.6",
"@nteract/dropdown-menu": "1.0.1",
"@nteract/editor": "10.1.2",
"@nteract/editor": "9.6.6",
"@nteract/fixtures": "2.3.0",
"@nteract/iron-icons": "1.0.0",
"@nteract/jupyter-widgets": "2.0.0",
"@nteract/logos": "1.0.0",
"@nteract/markdown": "4.4.0",
"@nteract/monaco-editor": "3.2.0",
"@nteract/monaco-editor": "3.0.3",
"@nteract/octicons": "2.0.0",
"@nteract/outputs": "3.0.9",
"@nteract/presentational-components": "3.0.7",
"@nteract/stateful-components": "1.7.0",
"@nteract/stateful-components": "1.4.0",
"@nteract/styles": "2.0.2",
"@nteract/transform-geojson": "5.1.8",
"@nteract/transform-model-debug": "5.0.1",
@@ -47,7 +47,6 @@
"copy-webpack-plugin": "6.0.2",
"crossroads": "0.12.2",
"css-element-queries": "1.1.1",
"d3": "6.1.1",
"datatables.net-colreorder-dt": "1.5.1",
"datatables.net-dt": "1.10.19",
"date-fns": "1.29.0",
@@ -74,7 +73,7 @@
"promise-polyfill": "8.1.0",
"promise.prototype.finally": "3.1.0",
"q": "1.5.1",
"react": "16.13.1",
"react": "16.9.0",
"react-animate-height": "2.0.8",
"react-dnd": "9.4.0",
"react-dnd-html5-backend": "9.4.0",
@@ -83,13 +82,12 @@
"react-notification-system": "0.2.17",
"react-redux": "7.1.3",
"redux": "4.0.4",
"rx-jupyter": "5.5.12",
"rxjs": "6.6.3",
"rx-jupyter": "5.5.2",
"rxjs": "6.5.3",
"styled-components": "4.3.2",
"text-encoding": "0.7.0",
"underscore": "1.9.1",
"url-polyfill": "1.1.7",
"utility-types": "3.10.0",
"webcrypto-liner": "1.1.4",
"webfontloader": "1.6.28",
"whatwg-fetch": "3.0.0"
@@ -102,9 +100,9 @@
"@types/applicationinsights-js": "1.0.7",
"@types/codemirror": "0.0.56",
"@types/crossroads": "0.0.30",
"@types/d3": "5.9.2",
"@types/enzyme": "3.10.7",
"@types/enzyme-adapter-react-16": "1.0.6",
"@types/d3": "4.13.2",
"@types/enzyme": "3.10.3",
"@types/enzyme-adapter-react-16": "1.0.5",
"@types/expect-puppeteer": "4.4.3",
"@types/hasher": "0.0.31",
"@types/jest": "23.3.10",
@@ -115,7 +113,7 @@
"@types/prop-types": "15.5.8",
"@types/puppeteer": "3.0.1",
"@types/q": "1.5.1",
"@types/react": "16.9.49",
"@types/react": "16.8.25",
"@types/react-dom": "16.0.7",
"@types/react-notification-system": "0.2.39",
"@types/react-redux": "7.1.7",
@@ -134,9 +132,10 @@
"case-sensitive-paths-webpack-plugin": "2.3.0",
"create-file-webpack": "1.0.2",
"css-loader": "1.0.0",
"enzyme": "3.11.0",
"enzyme-adapter-react-16": "1.15.5",
"enzyme-to-json": "3.6.1",
"d3": "4.13.0",
"enzyme": "3.10.0",
"enzyme-adapter-react-16": "1.15.1",
"enzyme-to-json": "3.4.3",
"eslint": "7.8.1",
"eslint-cli": "1.1.1",
"eslint-plugin-no-null": "1.0.2",
@@ -157,7 +156,7 @@
"less-vars-loader": "1.1.0",
"mini-css-extract-plugin": "0.4.3",
"monaco-editor-webpack-plugin": "1.7.0",
"node-fetch": "2.6.1",
"node-fetch": "2.6.0",
"prettier": "1.19.1",
"puppeteer": "4.0.0",
"raw-loader": "0.5.1",

View File

@@ -1,4 +1,5 @@
import { AutopilotTier } from "../Contracts/DataModels";
import { configContext } from "../ConfigContext";
import { HashMap } from "./HashMap";
export class AuthorizationEndpoints {
@@ -12,6 +13,12 @@ export class CodeOfConductEndpoints {
public static termsOfUse: string = "https://aka.ms/ms-terms-of-use";
}
export class BackendEndpoints {
public static localhost: string = "https://localhost:12900";
public static dev: string = "https://ext.documents-dev.windows-int.net";
public static productionPortal: string = configContext.BACKEND_ENDPOINT || "https://main.documentdb.ext.azure.com";
}
export class EndpointsRegex {
public static readonly cassandra = [
"AccountEndpoint=(.*).cassandra.cosmosdb.azure.com",
@@ -43,7 +50,7 @@ export class ArmApiVersions {
public static readonly arcadiaLivy: string = "2019-11-01-preview";
public static readonly arm: string = "2015-11-01";
public static readonly armFeatures: string = "2014-08-01-preview";
public static readonly publicVersion = "2020-04-01";
public static readonly publicVersion = "2020-03-01";
}
export class ArmResourceTypes {
@@ -117,6 +124,7 @@ export class Features {
public static readonly enableGalleryPublish = "enablegallerypublish";
public static readonly enableCodeOfConduct = "enablecodeofconduct";
public static readonly enableLinkInjection = "enablelinkinjection";
public static readonly enableSettingsV2 = "enablesettingsv2";
public static readonly enableSpark = "enablespark";
public static readonly livyEndpoint = "livyendpoint";
public static readonly notebookServerUrl = "notebookserverurl";
@@ -130,11 +138,6 @@ export class Features {
public static readonly enableSDKoperations = "enablesdkoperations";
}
// flight names returned from the portal are always lowercase
export class Flights {
public static readonly SettingsV2 = "settingsv2";
}
export class AfecFeatures {
public static readonly Spark = "spark-public-preview";
public static readonly Notebooks = "sparknotebooks-public-preview";

View File

@@ -14,9 +14,7 @@ describe("tokenProvider", () => {
};
beforeEach(() => {
updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
});
window.dataExplorer = { extensionEndpoint: () => "https://main.documentdb.ext.azure.com" } as any;
window.fetch = jest.fn().mockImplementation(() => {
return {
json: () => "{}",
@@ -60,9 +58,7 @@ describe("getTokenFromAuthService", () => {
});
it("builds the correct URL in production", () => {
updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
});
window.dataExplorer = { extensionEndpoint: () => "https://main.documentdb.ext.azure.com" } as any;
getTokenFromAuthService("GET", "dbs", "foo");
expect(window.fetch).toHaveBeenCalledWith(
"https://main.documentdb.ext.azure.com/api/guest/runtimeproxy/authorizationTokens",
@@ -112,6 +108,7 @@ describe("endpoint", () => {
describe("requestPlugin", () => {
beforeEach(() => {
delete window.dataExplorerPlatform;
resetConfigContext();
});

View File

@@ -52,7 +52,7 @@ export const endpoint = () => {
export async function getTokenFromAuthService(verb: string, resourceType: string, resourceId?: string): Promise<any> {
try {
const host = configContext.BACKEND_ENDPOINT;
const host = configContext.BACKEND_ENDPOINT || _global.dataExplorer.extensionEndpoint();
const response = await _global.fetch(host + "/api/guest/runtimeproxy/authorizationTokens", {
method: "POST",
headers: {
@@ -85,7 +85,8 @@ export function client(): Cosmos.CosmosClient {
userAgentSuffix: "Azure Portal"
};
if (configContext.PROXY_PATH !== undefined) {
// In development we proxy requests to the backend via webpack. This is removed in production bundles.
if (process.env.NODE_ENV === "development") {
(options as any).plugins = [{ on: "request", plugin: requestPlugin }];
}
return new Cosmos.CosmosClient(options);

View File

@@ -72,6 +72,22 @@ export function getPartitionKeyHeader(partitionKeyDefinition: DataModels.Partiti
return [partitionKeyValue];
}
export function updateOffer(
offer: DataModels.Offer,
newOffer: DataModels.Offer,
options?: RequestOptions
): Q.Promise<DataModels.Offer> {
return Q(
client()
.offer(offer.id)
// TODO Remove casting when SDK types are fixed (https://github.com/Azure/azure-sdk-for-js/issues/10660)
.replace((newOffer as unknown) as OfferDefinition, options)
.then(response => {
return Promise.all([refreshCachedOffers(), refreshCachedResources()]).then(() => response.resource);
})
);
}
export function updateDocument(
collection: ViewModels.CollectionBase,
documentId: DocumentId,

View File

@@ -157,6 +157,41 @@ export function updateDocument(
return deferred.promise;
}
export function updateOffer(
offer: DataModels.Offer,
newOffer: DataModels.Offer,
options: RequestOptions
): Q.Promise<DataModels.Offer> {
var deferred = Q.defer<any>();
const clearMessage = logConsoleProgress(`Updating offer for resource ${offer.resource}`);
DataAccessUtilityBase.updateOffer(offer, newOffer, options)
.then(
(replacedOffer: DataModels.Offer) => {
logConsoleInfo(`Successfully updated offer for resource ${offer.resource}`);
deferred.resolve(replacedOffer);
},
(error: any) => {
logConsoleError(`Error updating offer for resource ${offer.resource}: ${JSON.stringify(error)}`);
Logger.logError(
JSON.stringify({
oldOffer: offer,
newOffer: newOffer,
error: error
}),
"UpdateOffer",
error.code
);
sendNotificationForError(error);
deferred.reject(error);
}
)
.finally(() => {
clearMessage();
});
return deferred.promise;
}
export function createDocument(collection: ViewModels.CollectionBase, newDocument: any): Q.Promise<any> {
var deferred = Q.defer<any>();
const entityName = getEntityName();

View File

@@ -63,9 +63,10 @@ describe("MongoProxyClient", () => {
updateUserContext({
databaseAccount
});
updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
});
window.dataExplorer = {
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
serverId: () => ""
} as any;
window.fetch = jest.fn().mockImplementation(fetchMock);
});
afterEach(() => {
@@ -95,9 +96,10 @@ describe("MongoProxyClient", () => {
updateUserContext({
databaseAccount
});
updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
});
window.dataExplorer = {
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
serverId: () => ""
} as any;
window.fetch = jest.fn().mockImplementation(fetchMock);
});
afterEach(() => {
@@ -127,9 +129,10 @@ describe("MongoProxyClient", () => {
updateUserContext({
databaseAccount
});
updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
});
window.dataExplorer = {
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
serverId: () => ""
} as any;
window.fetch = jest.fn().mockImplementation(fetchMock);
});
afterEach(() => {
@@ -159,9 +162,10 @@ describe("MongoProxyClient", () => {
updateUserContext({
databaseAccount
});
updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
});
window.dataExplorer = {
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
serverId: () => ""
} as any;
window.fetch = jest.fn().mockImplementation(fetchMock);
});
afterEach(() => {
@@ -191,9 +195,10 @@ describe("MongoProxyClient", () => {
updateUserContext({
databaseAccount
});
updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
});
window.dataExplorer = {
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
serverId: () => ""
} as any;
window.fetch = jest.fn().mockImplementation(fetchMock);
});
afterEach(() => {
@@ -224,9 +229,10 @@ describe("MongoProxyClient", () => {
updateUserContext({
databaseAccount
});
updateConfigContext({
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
});
window.dataExplorer = {
extensionEndpoint: () => "https://main.documentdb.ext.azure.com",
serverId: () => ""
} as any;
});
it("returns a production endpoint", () => {

View File

@@ -327,7 +327,8 @@ export function createMongoCollectionWithProxy(
}
export function getEndpoint(): string {
let url = (configContext.MONGO_BACKEND_ENDPOINT || configContext.BACKEND_ENDPOINT) + "/api/mongo/explorer";
const extensionEndpoint = window.dataExplorer.extensionEndpoint();
let url = (configContext.MONGO_BACKEND_ENDPOINT || extensionEndpoint) + "/api/mongo/explorer";
if (window.authType === AuthType.EncryptedToken) {
url = url.replace("api/mongo", "api/guest/mongo");

View File

@@ -0,0 +1,46 @@
import "jquery";
import * as Q from "q";
import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels";
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
import { userContext } from "../UserContext";
export class NotificationsClientBase {
private _extensionEndpoint: string;
private _notificationsApiSuffix: string;
protected constructor(notificationsApiSuffix: string) {
this._notificationsApiSuffix = notificationsApiSuffix;
}
public fetchNotifications(): Q.Promise<DataModels.Notification[]> {
const deferred: Q.Deferred<DataModels.Notification[]> = Q.defer<DataModels.Notification[]>();
const databaseAccount = userContext.databaseAccount;
const subscriptionId = userContext.subscriptionId;
const resourceGroup = userContext.resourceGroup;
const url = `${this._extensionEndpoint}${this._notificationsApiSuffix}?accountName=${databaseAccount.name}&subscriptionId=${subscriptionId}&resourceGroup=${resourceGroup}`;
const authorizationHeader: ViewModels.AuthorizationTokenHeaderMetadata = getAuthorizationHeader();
const headers: any = {};
headers[authorizationHeader.header] = authorizationHeader.token;
$.ajax({
url: url,
type: "GET",
headers: headers,
cache: false
}).then(
(notifications: DataModels.Notification[], textStatus: string, xhr: JQueryXHR<any>) => {
deferred.resolve(notifications);
},
(xhr: JQueryXHR<any>, textStatus: string, error: any) => {
deferred.reject(xhr.responseText);
}
);
return deferred.promise;
}
public setExtensionEndpoint(extensionEndpoint: string): void {
this._extensionEndpoint = extensionEndpoint;
}
}

View File

@@ -1,41 +0,0 @@
import * as DataModels from "../Contracts/DataModels";
import * as ViewModels from "../Contracts/ViewModels";
import { getAuthorizationHeader } from "../Utils/AuthorizationUtils";
import { userContext } from "../UserContext";
import { configContext, Platform } from "../ConfigContext";
const notificationsPath = () => {
switch (configContext.platform) {
case Platform.Hosted:
return "/api/guest/notifications";
case Platform.Portal:
return "/api/notifications";
default:
throw new Error(`Unknown platform: ${configContext.platform}`);
}
};
export const fetchPortalNotifications = async (): Promise<DataModels.Notification[]> => {
if (configContext.platform === Platform.Emulator) {
return [];
}
const databaseAccount = userContext.databaseAccount;
const subscriptionId = userContext.subscriptionId;
const resourceGroup = userContext.resourceGroup;
const url = `${configContext.BACKEND_ENDPOINT}${notificationsPath()}?accountName=${
databaseAccount.name
}&subscriptionId=${subscriptionId}&resourceGroup=${resourceGroup}`;
const authorizationHeader: ViewModels.AuthorizationTokenHeaderMetadata = getAuthorizationHeader();
const headers = { [authorizationHeader.header]: authorizationHeader.token };
const response = await window.fetch(url, {
headers
});
if (!response.ok) {
throw new Error(await response.text());
}
return (await response.json()) as DataModels.Notification[];
};

View File

@@ -1,76 +1,28 @@
import { AuthType } from "../../AuthType";
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
import {
SqlStoredProcedureCreateUpdateParameters,
SqlStoredProcedureResource
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { client } from "../CosmosClient";
import {
createUpdateSqlStoredProcedure,
getSqlStoredProcedure
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
import { userContext } from "../../UserContext";
export async function createStoredProcedure(
databaseId: string,
collectionId: string,
storedProcedure: StoredProcedureDefinition
): Promise<StoredProcedureDefinition & Resource> {
let createdStoredProcedure: StoredProcedureDefinition & Resource;
const clearMessage = logConsoleProgress(`Creating stored procedure ${storedProcedure.id}`);
try {
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
try {
const getResponse = await getSqlStoredProcedure(
userContext.subscriptionId,
userContext.resourceGroup,
userContext.databaseAccount.name,
databaseId,
collectionId,
storedProcedure.id
);
if (getResponse?.properties?.resource) {
throw new Error(
`Create stored procedure failed: stored procedure with id ${storedProcedure.id} already exists`
);
}
} catch (error) {
if (error.code !== "NotFound") {
throw error;
}
}
const createSprocParams: SqlStoredProcedureCreateUpdateParameters = {
properties: {
resource: storedProcedure as SqlStoredProcedureResource,
options: {}
}
};
const rpResponse = await createUpdateSqlStoredProcedure(
userContext.subscriptionId,
userContext.resourceGroup,
userContext.databaseAccount.name,
databaseId,
collectionId,
storedProcedure.id,
createSprocParams
);
return rpResponse && (rpResponse.properties?.resource as StoredProcedureDefinition & Resource);
}
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.storedProcedures.create(storedProcedure);
return response?.resource;
createdStoredProcedure = response.resource;
} catch (error) {
logConsoleError(`Error while creating stored procedure ${storedProcedure.id}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "CreateStoredProcedure", error.code);
sendNotificationForError(error);
throw error;
} finally {
clearMessage();
}
clearMessage();
return createdStoredProcedure;
}

View File

@@ -1,71 +1,28 @@
import { AuthType } from "../../AuthType";
import { Resource, TriggerDefinition } from "@azure/cosmos";
import {
SqlTriggerCreateUpdateParameters,
SqlTriggerResource
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { client } from "../CosmosClient";
import { createUpdateSqlTrigger, getSqlTrigger } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
import { userContext } from "../../UserContext";
export async function createTrigger(
databaseId: string,
collectionId: string,
trigger: TriggerDefinition
): Promise<TriggerDefinition & Resource> {
let createdTrigger: TriggerDefinition & Resource;
const clearMessage = logConsoleProgress(`Creating trigger ${trigger.id}`);
try {
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
try {
const getResponse = await getSqlTrigger(
userContext.subscriptionId,
userContext.resourceGroup,
userContext.databaseAccount.name,
databaseId,
collectionId,
trigger.id
);
if (getResponse?.properties?.resource) {
throw new Error(`Create trigger failed: trigger with id ${trigger.id} already exists`);
}
} catch (error) {
if (error.code !== "NotFound") {
throw error;
}
}
const createTriggerParams: SqlTriggerCreateUpdateParameters = {
properties: {
resource: trigger as SqlTriggerResource,
options: {}
}
};
const rpResponse = await createUpdateSqlTrigger(
userContext.subscriptionId,
userContext.resourceGroup,
userContext.databaseAccount.name,
databaseId,
collectionId,
trigger.id,
createTriggerParams
);
return rpResponse && (rpResponse.properties?.resource as TriggerDefinition & Resource);
}
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.triggers.create(trigger);
return response.resource;
createdTrigger = response.resource;
} catch (error) {
logConsoleError(`Error while creating trigger ${trigger.id}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "CreateTrigger", error.code);
sendNotificationForError(error);
throw error;
} finally {
clearMessage();
}
clearMessage();
return createdTrigger;
}

View File

@@ -1,76 +1,28 @@
import { AuthType } from "../../AuthType";
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
import {
SqlUserDefinedFunctionCreateUpdateParameters,
SqlUserDefinedFunctionResource
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { client } from "../CosmosClient";
import {
createUpdateSqlUserDefinedFunction,
getSqlUserDefinedFunction
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
import { userContext } from "../../UserContext";
export async function createUserDefinedFunction(
databaseId: string,
collectionId: string,
userDefinedFunction: UserDefinedFunctionDefinition
): Promise<UserDefinedFunctionDefinition & Resource> {
let createdUserDefinedFunction: UserDefinedFunctionDefinition & Resource;
const clearMessage = logConsoleProgress(`Creating user defined function ${userDefinedFunction.id}`);
try {
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
try {
const getResponse = await getSqlUserDefinedFunction(
userContext.subscriptionId,
userContext.resourceGroup,
userContext.databaseAccount.name,
databaseId,
collectionId,
userDefinedFunction.id
);
if (getResponse?.properties?.resource) {
throw new Error(
`Create user defined function failed: user defined function with id ${userDefinedFunction.id} already exists`
);
}
} catch (error) {
if (error.code !== "NotFound") {
throw error;
}
}
const createUDFParams: SqlUserDefinedFunctionCreateUpdateParameters = {
properties: {
resource: userDefinedFunction as SqlUserDefinedFunctionResource,
options: {}
}
};
const rpResponse = await createUpdateSqlUserDefinedFunction(
userContext.subscriptionId,
userContext.resourceGroup,
userContext.databaseAccount.name,
databaseId,
collectionId,
userDefinedFunction.id,
createUDFParams
);
return rpResponse && (rpResponse.properties?.resource as UserDefinedFunctionDefinition & Resource);
}
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.userDefinedFunctions.create(userDefinedFunction);
return response?.resource;
createdUserDefinedFunction = response.resource;
} catch (error) {
logConsoleError(`Error while creating user defined function ${userDefinedFunction.id}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "CreateUserupdateUserDefinedFunction", error.code);
sendNotificationForError(error);
throw error;
} finally {
clearMessage();
}
clearMessage();
return createdUserDefinedFunction;
}

View File

@@ -1,10 +1,7 @@
import { AuthType } from "../../AuthType";
import { client } from "../CosmosClient";
import { deleteSqlStoredProcedure } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
import { userContext } from "../../UserContext";
export async function deleteStoredProcedure(
databaseId: string,
@@ -13,28 +10,17 @@ export async function deleteStoredProcedure(
): Promise<void> {
const clearMessage = logConsoleProgress(`Deleting stored procedure ${storedProcedureId}`);
try {
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
await deleteSqlStoredProcedure(
userContext.subscriptionId,
userContext.resourceGroup,
userContext.databaseAccount.name,
databaseId,
collectionId,
storedProcedureId
);
} else {
await client()
.database(databaseId)
.container(collectionId)
.scripts.storedProcedure(storedProcedureId)
.delete();
}
await client()
.database(databaseId)
.container(collectionId)
.scripts.storedProcedure(storedProcedureId)
.delete();
} catch (error) {
logConsoleError(`Error while deleting stored procedure ${storedProcedureId}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "DeleteStoredProcedure", error.code);
sendNotificationForError(error);
throw error;
} finally {
clearMessage();
}
clearMessage();
return undefined;
}

View File

@@ -1,36 +1,22 @@
import { AuthType } from "../../AuthType";
import { client } from "../CosmosClient";
import { deleteSqlTrigger } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
import { userContext } from "../../UserContext";
export async function deleteTrigger(databaseId: string, collectionId: string, triggerId: string): Promise<void> {
const clearMessage = logConsoleProgress(`Deleting trigger ${triggerId}`);
try {
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
await deleteSqlTrigger(
userContext.subscriptionId,
userContext.resourceGroup,
userContext.databaseAccount.name,
databaseId,
collectionId,
triggerId
);
} else {
await client()
.database(databaseId)
.container(collectionId)
.scripts.trigger(triggerId)
.delete();
}
await client()
.database(databaseId)
.container(collectionId)
.scripts.trigger(triggerId)
.delete();
} catch (error) {
logConsoleError(`Error while deleting trigger ${triggerId}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "DeleteTrigger", error.code);
sendNotificationForError(error);
throw error;
} finally {
clearMessage();
}
clearMessage();
return undefined;
}

View File

@@ -1,36 +1,22 @@
import { AuthType } from "../../AuthType";
import { client } from "../CosmosClient";
import { deleteSqlUserDefinedFunction } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
import { userContext } from "../../UserContext";
export async function deleteUserDefinedFunction(databaseId: string, collectionId: string, id: string): Promise<void> {
const clearMessage = logConsoleProgress(`Deleting user defined function ${id}`);
try {
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
await deleteSqlUserDefinedFunction(
userContext.subscriptionId,
userContext.resourceGroup,
userContext.databaseAccount.name,
databaseId,
collectionId,
id
);
} else {
await client()
.database(databaseId)
.container(collectionId)
.scripts.userDefinedFunction(id)
.delete();
}
await client()
.database(databaseId)
.container(collectionId)
.scripts.userDefinedFunction(id)
.delete();
} catch (error) {
logConsoleError(`Error while deleting user defined function ${id}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "DeleteUserDefinedFunction", error.code);
sendNotificationForError(error);
throw error;
} finally {
clearMessage();
}
clearMessage();
return undefined;
}

View File

@@ -20,14 +20,26 @@ export const readDatabaseOffer = async (
const clearMessage = logConsoleProgress(`Querying offer for database ${params.databaseId}`);
let offerId = params.offerId;
if (!offerId) {
offerId = await (window.authType === AuthType.AAD &&
!userContext.useSDKOperations &&
userContext.defaultExperience !== DefaultAccountExperienceType.Table
? getDatabaseOfferIdWithARM(params.databaseId)
: getDatabaseOfferIdWithSDK(params.databaseResourceId));
if (!offerId) {
clearMessage();
return undefined;
if (
window.authType === AuthType.AAD &&
!userContext.useSDKOperations &&
userContext.defaultExperience !== DefaultAccountExperienceType.Table
) {
try {
offerId = await getDatabaseOfferIdWithARM(params.databaseId);
} catch (error) {
clearMessage();
if (error.code !== "NotFound") {
throw new error();
}
return undefined;
}
} else {
offerId = await getDatabaseOfferIdWithSDK(params.databaseResourceId);
if (!offerId) {
clearMessage();
return undefined;
}
}
}
@@ -63,32 +75,24 @@ const getDatabaseOfferIdWithARM = async (databaseId: string): Promise<string> =>
const resourceGroup = userContext.resourceGroup;
const accountName = userContext.databaseAccount.name;
const defaultExperience = userContext.defaultExperience;
try {
switch (defaultExperience) {
case DefaultAccountExperienceType.DocumentDB:
rpResponse = await getSqlDatabaseThroughput(subscriptionId, resourceGroup, accountName, databaseId);
break;
case DefaultAccountExperienceType.MongoDB:
rpResponse = await getMongoDBDatabaseThroughput(subscriptionId, resourceGroup, accountName, databaseId);
break;
case DefaultAccountExperienceType.Cassandra:
rpResponse = await getCassandraKeyspaceThroughput(subscriptionId, resourceGroup, accountName, databaseId);
break;
case DefaultAccountExperienceType.Graph:
rpResponse = await getGremlinDatabaseThroughput(subscriptionId, resourceGroup, accountName, databaseId);
break;
default:
throw new Error(`Unsupported default experience type: ${defaultExperience}`);
}
return rpResponse?.name;
} catch (error) {
if (error.code !== "NotFound") {
throw error;
}
return undefined;
switch (defaultExperience) {
case DefaultAccountExperienceType.DocumentDB:
rpResponse = await getSqlDatabaseThroughput(subscriptionId, resourceGroup, accountName, databaseId);
break;
case DefaultAccountExperienceType.MongoDB:
rpResponse = await getMongoDBDatabaseThroughput(subscriptionId, resourceGroup, accountName, databaseId);
break;
case DefaultAccountExperienceType.Cassandra:
rpResponse = await getCassandraKeyspaceThroughput(subscriptionId, resourceGroup, accountName, databaseId);
break;
case DefaultAccountExperienceType.Graph:
rpResponse = await getGremlinDatabaseThroughput(subscriptionId, resourceGroup, accountName, databaseId);
break;
default:
throw new Error(`Unsupported default experience type: ${defaultExperience}`);
}
return rpResponse?.name;
};
const getDatabaseOfferIdWithSDK = async (databaseResourceId: string): Promise<string> => {

View File

@@ -18,7 +18,9 @@ export async function readDatabases(): Promise<DataModels.Database[]> {
if (
window.authType === AuthType.AAD &&
!userContext.useSDKOperations &&
userContext.defaultExperience !== DefaultAccountExperienceType.Table
userContext.defaultExperience !== DefaultAccountExperienceType.MongoDB &&
userContext.defaultExperience !== DefaultAccountExperienceType.Table &&
userContext.defaultExperience !== DefaultAccountExperienceType.Cassandra
) {
databases = await readDatabasesWithARM();
} else {

View File

@@ -13,13 +13,10 @@ export const readOffers = async (): Promise<Offer[]> => {
const clearMessage = logConsoleProgress(`Querying offers`);
try {
if (configContext.platform === Platform.Portal) {
const offers = sendCachedDataMessage<Offer[]>(MessageTypes.AllOffers, [
return sendCachedDataMessage<Offer[]>(MessageTypes.AllOffers, [
userContext.databaseAccount.id,
ClientDefaults.portalCacheTimeoutMs
]);
clearMessage();
return offers;
}
} catch (error) {
// If error getting cached Offers, continue on and read via SDK

View File

@@ -1,41 +1,27 @@
import { AuthType } from "../../AuthType";
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
import { client } from "../CosmosClient";
import { listSqlStoredProcedures } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
import { userContext } from "../../UserContext";
export async function readStoredProcedures(
databaseId: string,
collectionId: string
): Promise<(StoredProcedureDefinition & Resource)[]> {
let sprocs: (StoredProcedureDefinition & Resource)[];
const clearMessage = logConsoleProgress(`Querying stored procedures for container ${collectionId}`);
try {
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
const rpResponse = await listSqlStoredProcedures(
userContext.subscriptionId,
userContext.resourceGroup,
userContext.databaseAccount.name,
databaseId,
collectionId
);
return rpResponse?.value?.map(sproc => sproc.properties?.resource as StoredProcedureDefinition & Resource);
}
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.storedProcedures.readAll()
.fetchAll();
return response?.resources;
sprocs = response.resources;
} catch (error) {
logConsoleError(`Failed to query stored procedures for container ${collectionId}: ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "ReadStoredProcedures", error.code);
sendNotificationForError(error);
throw error;
} finally {
clearMessage();
}
clearMessage();
return sprocs;
}

View File

@@ -1,41 +1,27 @@
import { AuthType } from "../../AuthType";
import { Resource, TriggerDefinition } from "@azure/cosmos";
import { client } from "../CosmosClient";
import { listSqlTriggers } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
import { userContext } from "../../UserContext";
export async function readTriggers(
databaseId: string,
collectionId: string
): Promise<(TriggerDefinition & Resource)[]> {
let triggers: (TriggerDefinition & Resource)[];
const clearMessage = logConsoleProgress(`Querying triggers for container ${collectionId}`);
try {
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
const rpResponse = await listSqlTriggers(
userContext.subscriptionId,
userContext.resourceGroup,
userContext.databaseAccount.name,
databaseId,
collectionId
);
return rpResponse?.value?.map(trigger => trigger.properties?.resource as TriggerDefinition & Resource);
}
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.triggers.readAll()
.fetchAll();
return response?.resources;
triggers = response.resources;
} catch (error) {
logConsoleError(`Failed to query triggers for container ${collectionId}: ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "ReadTriggers", error.code);
sendNotificationForError(error);
throw error;
} finally {
clearMessage();
}
clearMessage();
return triggers;
}

View File

@@ -1,41 +1,27 @@
import { AuthType } from "../../AuthType";
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
import { client } from "../CosmosClient";
import { listSqlUserDefinedFunctions } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
import { userContext } from "../../UserContext";
export async function readUserDefinedFunctions(
databaseId: string,
collectionId: string
): Promise<(UserDefinedFunctionDefinition & Resource)[]> {
let udfs: (UserDefinedFunctionDefinition & Resource)[];
const clearMessage = logConsoleProgress(`Querying user defined functions for container ${collectionId}`);
try {
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
const rpResponse = await listSqlUserDefinedFunctions(
userContext.subscriptionId,
userContext.resourceGroup,
userContext.databaseAccount.name,
databaseId,
collectionId
);
return rpResponse?.value?.map(udf => udf.properties?.resource as UserDefinedFunctionDefinition & Resource);
}
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.userDefinedFunctions.readAll()
.fetchAll();
return response?.resources;
udfs = response.resources;
} catch (error) {
logConsoleError(`Failed to query user defined functions for container ${collectionId}: ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "ReadUserDefinedFunctions", error.code);
sendNotificationForError(error);
throw error;
} finally {
clearMessage();
}
clearMessage();
return udfs;
}

View File

@@ -41,7 +41,6 @@ export async function updateCollection(
try {
if (
window.authType === AuthType.AAD &&
!userContext.useSDKOperations &&
userContext.defaultExperience !== DefaultAccountExperienceType.MongoDB &&
userContext.defaultExperience !== DefaultAccountExperienceType.Table
) {
@@ -53,18 +52,16 @@ export async function updateCollection(
.replace(newCollection as ContainerDefinition, options);
collection = sdkResponse.resource as Collection;
}
logConsoleInfo(`Successfully updated container ${collectionId}`);
await refreshCachedResources();
return collection;
} catch (error) {
logConsoleError(`Failed to update container ${collectionId}: ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "UpdateCollection", error.code);
sendNotificationForError(error);
throw error;
} finally {
clearMessage();
}
logConsoleInfo(`Successfully updated container ${collectionId}`);
clearMessage();
await refreshCachedResources();
return collection;
}
async function updateCollectionWithARM(

View File

@@ -1,425 +0,0 @@
import { AuthType } from "../../AuthType";
import { DefaultAccountExperienceType } from "../../DefaultAccountExperienceType";
import { HttpHeaders } from "../Constants";
import { Offer, UpdateOfferParams } from "../../Contracts/DataModels";
import { OfferDefinition } from "@azure/cosmos";
import { RequestOptions } from "@azure/cosmos/dist-esm";
import { ThroughputSettingsUpdateParameters } from "../../Utils/arm/generatedClients/2020-04-01/types";
import { client } from "../CosmosClient";
import { logConsoleError, logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { logError } from "../Logger";
import { readCollectionOffer } from "./readCollectionOffer";
import { readDatabaseOffer } from "./readDatabaseOffer";
import { refreshCachedOffers, refreshCachedResources } from "../DataAccessUtilityBase";
import { sendNotificationForError } from "./sendNotificationForError";
import {
updateSqlDatabaseThroughput,
migrateSqlDatabaseToAutoscale,
migrateSqlDatabaseToManualThroughput,
migrateSqlContainerToAutoscale,
migrateSqlContainerToManualThroughput,
updateSqlContainerThroughput
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import {
updateCassandraKeyspaceThroughput,
migrateCassandraKeyspaceToAutoscale,
migrateCassandraKeyspaceToManualThroughput,
migrateCassandraTableToAutoscale,
migrateCassandraTableToManualThroughput,
updateCassandraTableThroughput
} from "../../Utils/arm/generatedClients/2020-04-01/cassandraResources";
import {
updateMongoDBDatabaseThroughput,
migrateMongoDBDatabaseToAutoscale,
migrateMongoDBDatabaseToManualThroughput,
migrateMongoDBCollectionToAutoscale,
migrateMongoDBCollectionToManualThroughput,
updateMongoDBCollectionThroughput
} from "../../Utils/arm/generatedClients/2020-04-01/mongoDBResources";
import {
updateGremlinDatabaseThroughput,
migrateGremlinDatabaseToAutoscale,
migrateGremlinDatabaseToManualThroughput,
migrateGremlinGraphToAutoscale,
migrateGremlinGraphToManualThroughput,
updateGremlinGraphThroughput
} from "../../Utils/arm/generatedClients/2020-04-01/gremlinResources";
import { userContext } from "../../UserContext";
import {
migrateTableToAutoscale,
migrateTableToManualThroughput,
updateTableThroughput
} from "../../Utils/arm/generatedClients/2020-04-01/tableResources";
export const updateOffer = async (params: UpdateOfferParams): Promise<Offer> => {
let updatedOffer: Offer;
const offerResourceText: string = params.collectionId
? `collection ${params.collectionId}`
: `database ${params.databaseId}`;
const clearMessage = logConsoleProgress(`Updating offer for ${offerResourceText}`);
try {
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
if (params.collectionId) {
updatedOffer = await updateCollectionOfferWithARM(params);
} else if (userContext.defaultExperience === DefaultAccountExperienceType.Table) {
// update table's database offer with SDK since RP doesn't support it
updatedOffer = await updateOfferWithSDK(params);
} else {
updatedOffer = await updateDatabaseOfferWithARM(params);
}
} else {
updatedOffer = await updateOfferWithSDK(params);
}
await refreshCachedOffers();
await refreshCachedResources();
logConsoleInfo(`Successfully updated offer for ${offerResourceText}`);
return updatedOffer;
} catch (error) {
logConsoleError(`Error updating offer for ${offerResourceText}: ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "UpdateCollection", error.code);
sendNotificationForError(error);
throw error;
} finally {
clearMessage();
}
};
const updateCollectionOfferWithARM = async (params: UpdateOfferParams): Promise<Offer> => {
try {
switch (userContext.defaultExperience) {
case DefaultAccountExperienceType.DocumentDB:
await updateSqlContainerOffer(params);
break;
case DefaultAccountExperienceType.MongoDB:
await updateMongoCollectionOffer(params);
break;
case DefaultAccountExperienceType.Cassandra:
await updateCassandraTableOffer(params);
break;
case DefaultAccountExperienceType.Graph:
await updateGremlinGraphOffer(params);
break;
case DefaultAccountExperienceType.Table:
await updateTableOffer(params);
break;
default:
throw new Error(`Unsupported default experience type: ${userContext.defaultExperience}`);
}
} catch (error) {
if (error.code !== "MethodNotAllowed") {
throw error;
}
}
return await readCollectionOffer({
collectionId: params.collectionId,
databaseId: params.databaseId,
offerId: params.currentOffer.id
});
};
const updateDatabaseOfferWithARM = async (params: UpdateOfferParams): Promise<Offer> => {
try {
switch (userContext.defaultExperience) {
case DefaultAccountExperienceType.DocumentDB:
await updateSqlDatabaseOffer(params);
break;
case DefaultAccountExperienceType.MongoDB:
await updateMongoDatabaseOffer(params);
break;
case DefaultAccountExperienceType.Cassandra:
await updateCassandraKeyspaceOffer(params);
break;
case DefaultAccountExperienceType.Graph:
await updateGremlinDatabaseOffer(params);
break;
default:
throw new Error(`Unsupported default experience type: ${userContext.defaultExperience}`);
}
} catch (error) {
if (error.code !== "MethodNotAllowed") {
throw error;
}
}
return await readDatabaseOffer({
databaseId: params.databaseId,
offerId: params.currentOffer.id
});
};
const updateSqlContainerOffer = async (params: UpdateOfferParams): Promise<void> => {
const subscriptionId = userContext.subscriptionId;
const resourceGroup = userContext.resourceGroup;
const accountName = userContext.databaseAccount.name;
if (params.migrateToAutoPilot) {
await migrateSqlContainerToAutoscale(
subscriptionId,
resourceGroup,
accountName,
params.databaseId,
params.collectionId
);
} else if (params.migrateToManual) {
await migrateSqlContainerToManualThroughput(
subscriptionId,
resourceGroup,
accountName,
params.databaseId,
params.collectionId
);
} else {
const body: ThroughputSettingsUpdateParameters = createUpdateOfferBody(params);
await updateSqlContainerThroughput(
subscriptionId,
resourceGroup,
accountName,
params.databaseId,
params.collectionId,
body
);
}
};
const updateMongoCollectionOffer = async (params: UpdateOfferParams): Promise<void> => {
const subscriptionId = userContext.subscriptionId;
const resourceGroup = userContext.resourceGroup;
const accountName = userContext.databaseAccount.name;
if (params.migrateToAutoPilot) {
await migrateMongoDBCollectionToAutoscale(
subscriptionId,
resourceGroup,
accountName,
params.databaseId,
params.collectionId
);
} else if (params.migrateToManual) {
await migrateMongoDBCollectionToManualThroughput(
subscriptionId,
resourceGroup,
accountName,
params.databaseId,
params.collectionId
);
} else {
const body: ThroughputSettingsUpdateParameters = createUpdateOfferBody(params);
await updateMongoDBCollectionThroughput(
subscriptionId,
resourceGroup,
accountName,
params.databaseId,
params.collectionId,
body
);
}
};
const updateCassandraTableOffer = async (params: UpdateOfferParams): Promise<void> => {
const subscriptionId = userContext.subscriptionId;
const resourceGroup = userContext.resourceGroup;
const accountName = userContext.databaseAccount.name;
if (params.migrateToAutoPilot) {
await migrateCassandraTableToAutoscale(
subscriptionId,
resourceGroup,
accountName,
params.databaseId,
params.collectionId
);
} else if (params.migrateToManual) {
await migrateCassandraTableToManualThroughput(
subscriptionId,
resourceGroup,
accountName,
params.databaseId,
params.collectionId
);
} else {
const body: ThroughputSettingsUpdateParameters = createUpdateOfferBody(params);
await updateCassandraTableThroughput(
subscriptionId,
resourceGroup,
accountName,
params.databaseId,
params.collectionId,
body
);
}
};
const updateGremlinGraphOffer = async (params: UpdateOfferParams): Promise<void> => {
const subscriptionId = userContext.subscriptionId;
const resourceGroup = userContext.resourceGroup;
const accountName = userContext.databaseAccount.name;
if (params.migrateToAutoPilot) {
await migrateGremlinGraphToAutoscale(
subscriptionId,
resourceGroup,
accountName,
params.databaseId,
params.collectionId
);
} else if (params.migrateToManual) {
await migrateGremlinGraphToManualThroughput(
subscriptionId,
resourceGroup,
accountName,
params.databaseId,
params.collectionId
);
} else {
const body: ThroughputSettingsUpdateParameters = createUpdateOfferBody(params);
await updateGremlinGraphThroughput(
subscriptionId,
resourceGroup,
accountName,
params.databaseId,
params.collectionId,
body
);
}
};
const updateTableOffer = async (params: UpdateOfferParams): Promise<void> => {
const subscriptionId = userContext.subscriptionId;
const resourceGroup = userContext.resourceGroup;
const accountName = userContext.databaseAccount.name;
if (params.migrateToAutoPilot) {
await migrateTableToAutoscale(subscriptionId, resourceGroup, accountName, params.collectionId);
} else if (params.migrateToManual) {
await migrateTableToManualThroughput(subscriptionId, resourceGroup, accountName, params.collectionId);
} else {
const body: ThroughputSettingsUpdateParameters = createUpdateOfferBody(params);
await updateTableThroughput(subscriptionId, resourceGroup, accountName, params.collectionId, body);
}
};
const updateSqlDatabaseOffer = async (params: UpdateOfferParams): Promise<void> => {
const subscriptionId = userContext.subscriptionId;
const resourceGroup = userContext.resourceGroup;
const accountName = userContext.databaseAccount.name;
if (params.migrateToAutoPilot) {
await migrateSqlDatabaseToAutoscale(subscriptionId, resourceGroup, accountName, params.databaseId);
} else if (params.migrateToManual) {
await migrateSqlDatabaseToManualThroughput(subscriptionId, resourceGroup, accountName, params.databaseId);
} else {
const body: ThroughputSettingsUpdateParameters = createUpdateOfferBody(params);
await updateSqlDatabaseThroughput(subscriptionId, resourceGroup, accountName, params.databaseId, body);
}
};
const updateMongoDatabaseOffer = async (params: UpdateOfferParams): Promise<void> => {
const subscriptionId = userContext.subscriptionId;
const resourceGroup = userContext.resourceGroup;
const accountName = userContext.databaseAccount.name;
if (params.migrateToAutoPilot) {
await migrateMongoDBDatabaseToAutoscale(subscriptionId, resourceGroup, accountName, params.databaseId);
} else if (params.migrateToManual) {
await migrateMongoDBDatabaseToManualThroughput(subscriptionId, resourceGroup, accountName, params.databaseId);
} else {
const body: ThroughputSettingsUpdateParameters = createUpdateOfferBody(params);
await updateMongoDBDatabaseThroughput(subscriptionId, resourceGroup, accountName, params.databaseId, body);
}
};
const updateCassandraKeyspaceOffer = async (params: UpdateOfferParams): Promise<void> => {
const subscriptionId = userContext.subscriptionId;
const resourceGroup = userContext.resourceGroup;
const accountName = userContext.databaseAccount.name;
if (params.migrateToAutoPilot) {
await migrateCassandraKeyspaceToAutoscale(subscriptionId, resourceGroup, accountName, params.databaseId);
} else if (params.migrateToManual) {
await migrateCassandraKeyspaceToManualThroughput(subscriptionId, resourceGroup, accountName, params.databaseId);
} else {
const body: ThroughputSettingsUpdateParameters = createUpdateOfferBody(params);
await updateCassandraKeyspaceThroughput(subscriptionId, resourceGroup, accountName, params.databaseId, body);
}
};
const updateGremlinDatabaseOffer = async (params: UpdateOfferParams): Promise<void> => {
const subscriptionId = userContext.subscriptionId;
const resourceGroup = userContext.resourceGroup;
const accountName = userContext.databaseAccount.name;
if (params.migrateToAutoPilot) {
await migrateGremlinDatabaseToAutoscale(subscriptionId, resourceGroup, accountName, params.databaseId);
} else if (params.migrateToManual) {
await migrateGremlinDatabaseToManualThroughput(subscriptionId, resourceGroup, accountName, params.databaseId);
} else {
const body: ThroughputSettingsUpdateParameters = createUpdateOfferBody(params);
await updateGremlinDatabaseThroughput(subscriptionId, resourceGroup, accountName, params.databaseId, body);
}
};
const createUpdateOfferBody = (params: UpdateOfferParams): ThroughputSettingsUpdateParameters => {
const body: ThroughputSettingsUpdateParameters = {
properties: {
resource: {}
}
};
if (params.autopilotThroughput) {
body.properties.resource.autoscaleSettings = {
maxThroughput: params.autopilotThroughput
};
} else {
body.properties.resource.throughput = params.manualThroughput;
}
return body;
};
const updateOfferWithSDK = async (params: UpdateOfferParams): Promise<Offer> => {
const currentOffer = params.currentOffer;
const newOffer: Offer = {
content: {
offerThroughput: undefined,
offerIsRUPerMinuteThroughputEnabled: false
},
_etag: undefined,
_ts: undefined,
_rid: currentOffer._rid,
_self: currentOffer._self,
id: currentOffer.id,
offerResourceId: currentOffer.offerResourceId,
offerVersion: currentOffer.offerVersion,
offerType: currentOffer.offerType,
resource: currentOffer.resource
};
if (params.autopilotThroughput) {
newOffer.content.offerAutopilotSettings = {
maxThroughput: params.autopilotThroughput
};
} else {
newOffer.content.offerThroughput = params.manualThroughput;
}
const options: RequestOptions = {};
if (params.migrateToAutoPilot) {
options.initialHeaders = {
[HttpHeaders.migrateOfferToAutopilot]: "true"
};
delete newOffer.content.offerAutopilotSettings;
} else if (params.migrateToManual) {
options.initialHeaders = {
[HttpHeaders.migrateOfferToManualThroughput]: "true"
};
newOffer.content.offerAutopilotSettings = { maxThroughput: 0 };
}
const sdkResponse = await client()
.offer(params.currentOffer.id)
// TODO Remove casting when SDK types are fixed (https://github.com/Azure/azure-sdk-for-js/issues/10660)
.replace((newOffer as unknown) as OfferDefinition, options);
return sdkResponse?.resource;
};

View File

@@ -9,7 +9,8 @@ describe("updateOfferThroughputBeyondLimit", () => {
});
window.dataExplorer = {
logConsoleData: jest.fn(),
deleteInProgressConsoleDataWithId: jest.fn()
deleteInProgressConsoleDataWithId: jest.fn(),
extensionEndpoint: jest.fn()
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any;
await updateOfferThroughputBeyondLimit({

View File

@@ -28,7 +28,8 @@ export async function updateOfferThroughputBeyondLimit(request: UpdateOfferThrou
`Requesting increase in throughput to ${request.throughput} for ${resourceDescriptionInfo}`
);
const url = `${configContext.BACKEND_ENDPOINT}/api/offerthroughputrequest/updatebeyondspecifiedlimit`;
const explorer = window.dataExplorer;
const url = `${explorer.extensionEndpoint()}/api/offerthroughputrequest/updatebeyondspecifiedlimit`;
const authorizationHeader = getAuthorizationHeader();
const response = await fetch(url, {

View File

@@ -1,71 +1,29 @@
import { AuthType } from "../../AuthType";
import { Resource, StoredProcedureDefinition } from "@azure/cosmos";
import {
SqlStoredProcedureCreateUpdateParameters,
SqlStoredProcedureResource
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { client } from "../CosmosClient";
import {
createUpdateSqlStoredProcedure,
getSqlStoredProcedure
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
import { userContext } from "../../UserContext";
export async function updateStoredProcedure(
databaseId: string,
collectionId: string,
storedProcedure: StoredProcedureDefinition
): Promise<StoredProcedureDefinition & Resource> {
let updatedStoredProcedure: StoredProcedureDefinition & Resource;
const clearMessage = logConsoleProgress(`Updating stored procedure ${storedProcedure.id}`);
try {
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
const getResponse = await getSqlStoredProcedure(
userContext.subscriptionId,
userContext.resourceGroup,
userContext.databaseAccount.name,
databaseId,
collectionId,
storedProcedure.id
);
if (getResponse?.properties?.resource) {
const createSprocParams: SqlStoredProcedureCreateUpdateParameters = {
properties: {
resource: storedProcedure as SqlStoredProcedureResource,
options: {}
}
};
const rpResponse = await createUpdateSqlStoredProcedure(
userContext.subscriptionId,
userContext.resourceGroup,
userContext.databaseAccount.name,
databaseId,
collectionId,
storedProcedure.id,
createSprocParams
);
return rpResponse && (rpResponse.properties?.resource as StoredProcedureDefinition & Resource);
}
throw new Error(`Failed to update stored procedure: ${storedProcedure.id} does not exist.`);
}
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.storedProcedure(storedProcedure.id)
.replace(storedProcedure);
return response?.resource;
updatedStoredProcedure = response.resource;
} catch (error) {
const errorMessage = error.code === "NotFound" ? `${storedProcedure.id} does not exist.` : JSON.stringify(error);
logConsoleError(`Error while updating stored procedure ${storedProcedure.id}:\n ${errorMessage}`);
logError(errorMessage, "UpdateStoredProcedure", error.code);
logConsoleError(`Error while updating stored procedure ${storedProcedure.id}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "UpdateStoredProcedure", error.code);
sendNotificationForError(error);
throw error;
} finally {
clearMessage();
}
clearMessage();
return updatedStoredProcedure;
}

View File

@@ -1,68 +1,29 @@
import { AuthType } from "../../AuthType";
import {
SqlTriggerCreateUpdateParameters,
SqlTriggerResource
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { TriggerDefinition } from "@azure/cosmos";
import { client } from "../CosmosClient";
import { createUpdateSqlTrigger, getSqlTrigger } from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
import { userContext } from "../../UserContext";
export async function updateTrigger(
databaseId: string,
collectionId: string,
trigger: TriggerDefinition
): Promise<TriggerDefinition> {
let updatedTrigger: TriggerDefinition;
const clearMessage = logConsoleProgress(`Updating trigger ${trigger.id}`);
try {
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
const getResponse = await getSqlTrigger(
userContext.subscriptionId,
userContext.resourceGroup,
userContext.databaseAccount.name,
databaseId,
collectionId,
trigger.id
);
if (getResponse?.properties?.resource) {
const createTriggerParams: SqlTriggerCreateUpdateParameters = {
properties: {
resource: trigger as SqlTriggerResource,
options: {}
}
};
const rpResponse = await createUpdateSqlTrigger(
userContext.subscriptionId,
userContext.resourceGroup,
userContext.databaseAccount.name,
databaseId,
collectionId,
trigger.id,
createTriggerParams
);
return rpResponse && (rpResponse.properties?.resource as TriggerDefinition);
}
throw new Error(`Failed to update trigger: ${trigger.id} does not exist.`);
}
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.trigger(trigger.id)
.replace(trigger);
return response?.resource;
updatedTrigger = response.resource;
} catch (error) {
const errorMessage = error.code === "NotFound" ? `${trigger.id} does not exist.` : JSON.stringify(error);
logConsoleError(`Error while updating trigger ${trigger.id}:\n ${errorMessage}`);
logError(errorMessage, "UpdateTrigger", error.code);
logConsoleError(`Error while updating trigger ${trigger.id}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "UpdateTrigger", error.code);
sendNotificationForError(error);
throw error;
} finally {
clearMessage();
}
clearMessage();
return updatedTrigger;
}

View File

@@ -1,72 +1,29 @@
import { AuthType } from "../../AuthType";
import { Resource, UserDefinedFunctionDefinition } from "@azure/cosmos";
import {
SqlUserDefinedFunctionCreateUpdateParameters,
SqlUserDefinedFunctionResource
} from "../../Utils/arm/generatedClients/2020-04-01/types";
import { client } from "../CosmosClient";
import {
createUpdateSqlUserDefinedFunction,
getSqlUserDefinedFunction
} from "../../Utils/arm/generatedClients/2020-04-01/sqlResources";
import { logConsoleError, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { client } from "../CosmosClient";
import { logError } from "../Logger";
import { sendNotificationForError } from "./sendNotificationForError";
import { userContext } from "../../UserContext";
export async function updateUserDefinedFunction(
databaseId: string,
collectionId: string,
userDefinedFunction: UserDefinedFunctionDefinition
): Promise<UserDefinedFunctionDefinition & Resource> {
let updatedUserDefinedFunction: UserDefinedFunctionDefinition & Resource;
const clearMessage = logConsoleProgress(`Updating user defined function ${userDefinedFunction.id}`);
try {
if (window.authType === AuthType.AAD && !userContext.useSDKOperations) {
const getResponse = await getSqlUserDefinedFunction(
userContext.subscriptionId,
userContext.resourceGroup,
userContext.databaseAccount.name,
databaseId,
collectionId,
userDefinedFunction.id
);
if (getResponse?.properties?.resource) {
const createUDFParams: SqlUserDefinedFunctionCreateUpdateParameters = {
properties: {
resource: userDefinedFunction as SqlUserDefinedFunctionResource,
options: {}
}
};
const rpResponse = await createUpdateSqlUserDefinedFunction(
userContext.subscriptionId,
userContext.resourceGroup,
userContext.databaseAccount.name,
databaseId,
collectionId,
userDefinedFunction.id,
createUDFParams
);
return rpResponse && (rpResponse.properties?.resource as UserDefinedFunctionDefinition & Resource);
}
throw new Error(`Failed to update user defined function: ${userDefinedFunction.id} does not exist.`);
}
const response = await client()
.database(databaseId)
.container(collectionId)
.scripts.userDefinedFunction(userDefinedFunction.id)
.replace(userDefinedFunction);
return response?.resource;
updatedUserDefinedFunction = response.resource;
} catch (error) {
const errorMessage =
error.code === "NotFound" ? `${userDefinedFunction.id} does not exist.` : JSON.stringify(error);
logConsoleError(`Error while updating user defined function ${userDefinedFunction.id}:\n ${errorMessage}`);
logError(errorMessage, "UpdateUserupdateUserDefinedFunction", error.code);
logConsoleError(`Error while updating user defined function ${userDefinedFunction.id}:\n ${JSON.stringify(error)}`);
logError(JSON.stringify(error), "UpdateUserupdateUserDefinedFunction", error.code);
sendNotificationForError(error);
throw error;
} finally {
clearMessage();
}
clearMessage();
return updatedUserDefinedFunction;
}

View File

@@ -25,7 +25,6 @@ interface ConfigContext {
GITHUB_CLIENT_ID: string;
GITHUB_CLIENT_SECRET?: string; // No need to inject secret for prod. Juno already knows it.
hostedExplorerURL: string;
armAPIVersion?: string;
}
// Default configuration
@@ -34,7 +33,6 @@ let configContext: Readonly<ConfigContext> = {
allowedParentFrameOrigins: [
`^https:\\/\\/cosmos\\.azure\\.(com|cn|us)$`,
`^https:\\/\\/[\\.\\w]*portal\\.azure\\.(com|cn|us)$`,
`^https:\\/\\/[\\.\\w]*portal\\.microsoftazure.de$`,
`^https:\\/\\/[\\.\\w]*ext\\.azure\\.(com|cn|us)$`,
`^https:\\/\\/[\\.\\w]*\\.ext\\.microsoftazure\\.de$`,
`^https://cosmos-db-dataexplorer-germanycentral.azurewebsites.de$`
@@ -51,8 +49,7 @@ let configContext: Readonly<ConfigContext> = {
ARCADIA_ENDPOINT: "https://workspaceartifacts.projectarcadia.net",
ARCADIA_LIVY_ENDPOINT_DNS_ZONE: "dev.azuresynapse.net",
GITHUB_CLIENT_ID: "6cb2f63cf6f7b5cbdeca", // Registered OAuth app: https://github.com/settings/applications/1189306
JUNO_ENDPOINT: "https://tools.cosmos.azure.com",
BACKEND_ENDPOINT: "https://main.documentdb.ext.azure.com"
JUNO_ENDPOINT: "https://tools.cosmos.azure.com"
};
export function resetConfigContext(): void {
@@ -96,10 +93,6 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
}
// Allow override of platform value with URL query parameter
const params = new URLSearchParams(window.location.search);
if (params.has("armAPIVersion")) {
const armAPIVersion = params.get("armAPIVersion") || "";
updateConfigContext({ armAPIVersion });
}
if (params.has("platform")) {
const platform = params.get("platform");
switch (platform) {

View File

@@ -303,16 +303,6 @@ export interface ReadCollectionOfferParams {
offerId?: string;
}
export interface UpdateOfferParams {
currentOffer: Offer;
databaseId: string;
autopilotThroughput: number;
manualThroughput: number;
collectionId?: string;
migrateToAutoPilot?: boolean;
migrateToManual?: boolean;
}
export interface Notification {
id: string;
kind: string;

View File

@@ -85,7 +85,7 @@ export interface Database extends TreeNode {
collapseDatabase(): void;
loadCollections(): Promise<void>;
findCollectionWithId(collectionId: string): Collection;
findCollectionWithId(collectionRid: string): Collection;
openAddCollection(database: Database, event: MouseEvent): void;
onDeleteDatabaseContextMenuClick(source: Database, event: MouseEvent | KeyboardEvent): void;
onSettingsClick: () => void;
@@ -266,6 +266,7 @@ export interface TabOptions {
tabKind: CollectionTabKind;
title: string;
tabPath: string;
selfLink: string;
isActive: ko.Observable<boolean>;
hashLocation: string;
onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]) => void;
@@ -304,6 +305,7 @@ export interface QueryTabOptions extends TabOptions {
export interface ScriptTabOption extends TabOptions {
resource: any;
isNew: boolean;
collectionSelfLink?: string;
partitionKey?: DataModels.PartitionKey;
}
@@ -386,7 +388,6 @@ export interface DataExplorerInputsFrame {
dataExplorerVersion?: string;
isAuthWithresourceToken?: boolean;
defaultCollectionThroughput?: CollectionCreationDefaults;
flights?: readonly string[];
}
export interface CollectionCreationDefaults {

42
src/Definitions/jquery.contextmenu.d.ts vendored Normal file
View File

@@ -0,0 +1,42 @@
// Type definitions for jQuery contextMenu 1.7.0
// Project: http://medialize.github.com/jQuery-contextMenu/
// Definitions by: Natan Vivo <https://github.com/nvivo/>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
/// <reference path="jquery.d.ts" />
interface JQueryContextMenuOptions {
selector: string;
appendTo?: string;
trigger?: string;
autoHide?: boolean;
delay?: number;
determinePosition?: (menu: JQuery) => void;
position?: (opt: JQuery, x: number, y: number) => void;
positionSubmenu?: (menu: JQuery) => void;
zIndex?: number;
animation?: {
duration?: number;
show?: string;
hide?: string;
};
events?: {
show?: () => void;
hide?: () => void;
};
callback?: (key: any, options: any) => any;
items?: any;
build?: (triggerElement: JQuery, e: Event) => any;
reposition?: boolean;
className?: string;
itemClickEvent?: string;
}
interface JQueryStatic {
contextMenu(options?: JQueryContextMenuOptions): JQuery;
contextMenu(type: string, selector?: any): JQuery;
}
interface JQuery {
contextMenu(options?: any): JQuery;
}

View File

@@ -3,7 +3,7 @@ import { Dialog, DialogType, DialogFooter, IDialogProps } from "office-ui-fabric
import { IButtonProps, PrimaryButton, DefaultButton } from "office-ui-fabric-react/lib/Button";
import { ITextFieldProps, TextField } from "office-ui-fabric-react/lib/TextField";
import { Link } from "office-ui-fabric-react/lib/Link";
import { ChoiceGroup, FontIcon, IChoiceGroupProps } from "office-ui-fabric-react";
import { FontIcon } from "office-ui-fabric-react";
export interface TextFieldProps extends ITextFieldProps {
label: string;
@@ -24,7 +24,6 @@ export interface DialogProps {
subText: string;
isModal: boolean;
visible: boolean;
choiceGroupProps?: IChoiceGroupProps;
textFieldProps?: TextFieldProps;
linkProps?: LinkProps;
primaryButtonText: string;
@@ -66,7 +65,6 @@ export class DialogComponent extends React.Component<DialogProps, {}> {
minWidth: DIALOG_MIN_WIDTH,
maxWidth: DIALOG_MAX_WIDTH
};
const choiceGroupProps: IChoiceGroupProps = this.props.choiceGroupProps;
const textFieldProps: ITextFieldProps = this.props.textFieldProps;
const linkProps: LinkProps = this.props.linkProps;
const primaryButtonProps: IButtonProps = {
@@ -84,7 +82,6 @@ export class DialogComponent extends React.Component<DialogProps, {}> {
return (
<Dialog {...dialogProps}>
{choiceGroupProps && <ChoiceGroup {...choiceGroupProps} />}
{textFieldProps && <TextField {...textFieldProps} />}
{linkProps && (
<Link href={linkProps.linkUrl} target="_blank">

View File

@@ -55,6 +55,7 @@ export const FeaturePanelComponent: React.FunctionComponent = () => {
label: "Enable Injecting Notebook Viewer Link into the first cell",
value: "true"
},
{ key: "feature.enablesettingsv2", label: "Enable SettingsV2 Tab", value: "true" },
{ key: "feature.canexceedmaximumvalue", label: "Can exceed max value", value: "true" },
{
key: "feature.enablefixedcollectionwithsharedthroughput",

View File

@@ -178,6 +178,12 @@ exports[`Feature panel renders all flags 1`] = `
className="checkboxRow"
horizontalAlign="space-between"
>
<StyledCheckboxBase
checked={false}
key="feature.enablesettingsv2"
label="Enable SettingsV2 Tab"
onChange={[Function]}
/>
<StyledCheckboxBase
checked={false}
key="feature.canexceedmaximumvalue"

View File

@@ -18,9 +18,7 @@ describe("GalleryCardComponent", () => {
downloads: 0,
favorites: 0,
views: 0,
newCellId: undefined,
policyViolations: undefined,
pendingScanJobIds: undefined
newCellId: undefined
},
isFavorite: false,
showDownload: true,

View File

@@ -26,7 +26,7 @@ exports[`GalleryCardComponent renders 1`] = `
/>
</CardItem>
<CardItem>
<StyledImageBase
<Memo(StyledImageBase)
alt="name cover image"
height={144}
imageFit={2}
@@ -95,7 +95,7 @@ exports[`GalleryCardComponent renders 1`] = `
}
variant="tiny"
>
<StyledIconBase
<Memo(StyledIconBase)
iconName="RedEye"
styles={
Object {
@@ -119,7 +119,7 @@ exports[`GalleryCardComponent renders 1`] = `
}
variant="tiny"
>
<StyledIconBase
<Memo(StyledIconBase)
iconName="Download"
styles={
Object {
@@ -143,7 +143,7 @@ exports[`GalleryCardComponent renders 1`] = `
}
variant="tiny"
>
<StyledIconBase
<Memo(StyledIconBase)
iconName="Heart"
styles={
Object {

View File

@@ -1,25 +1,31 @@
jest.mock("../../../Juno/JunoClient");
import { shallow } from "enzyme";
import * as sinon from "sinon";
import React from "react";
import { CodeOfConductComponent, CodeOfConductComponentProps } from "./CodeOfConductComponent";
import { JunoClient } from "../../../Juno/JunoClient";
import { IJunoResponse, JunoClient } from "../../../Juno/JunoClient";
import { HttpStatusCodes } from "../../../Common/Constants";
describe("CodeOfConductComponent", () => {
let sandbox: sinon.SinonSandbox;
let codeOfConductProps: CodeOfConductComponentProps;
beforeEach(() => {
const junoClient = new JunoClient(undefined);
junoClient.acceptCodeOfConduct = jest.fn().mockReturnValue({
sandbox = sinon.sandbox.create();
sandbox.stub(JunoClient.prototype, "acceptCodeOfConduct").returns({
status: HttpStatusCodes.OK,
data: true
});
} as IJunoResponse<boolean>);
const junoClient = new JunoClient(undefined);
codeOfConductProps = {
junoClient: junoClient,
onAcceptCodeOfConduct: jest.fn()
};
});
afterEach(() => {
sandbox.restore();
});
it("renders", () => {
const wrapper = shallow(<CodeOfConductComponent {...codeOfConductProps} />);
expect(wrapper).toMatchSnapshot();

View File

@@ -1,7 +1,6 @@
import {
Dropdown,
FocusZone,
FontWeights,
IDropdownOption,
IPageSpecification,
IPivotItemProps,
@@ -12,8 +11,7 @@ import {
Pivot,
PivotItem,
SearchBox,
Stack,
Text
Stack
} from "office-ui-fabric-react";
import * as React from "react";
import * as Logger from "../../../Common/Logger";
@@ -153,7 +151,7 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
// explicitly checking if isCodeOfConductAccepted is not false, as it is initially undefined.
// Displaying code of conduct component on gallery load should not be the default behavior.
if (this.state.isCodeOfConductAccepted !== false) {
tabs.push(this.createPublishedNotebooksTab(GalleryTab.Published, this.state.publishedNotebooks));
tabs.push(this.createTab(GalleryTab.Published, this.state.publishedNotebooks));
}
}
@@ -199,59 +197,10 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
private createTab(tab: GalleryTab, data: IGalleryItem[]): GalleryTabInfo {
return {
tab,
content: this.createSearchBarHeader(this.createCardsTabContent(data))
content: this.createTabContent(data)
};
}
private createPublishedNotebooksTab = (tab: GalleryTab, data: IGalleryItem[]): GalleryTabInfo => {
return {
tab,
content: this.createPublishedNotebooksTabContent(data)
};
};
private createPublishedNotebooksTabContent = (data: IGalleryItem[]): JSX.Element => {
const { published, underReview, removed } = GalleryUtils.filterPublishedNotebooks(data);
const content = (
<Stack tokens={{ childrenGap: 10 }}>
{published?.length > 0 &&
this.createPublishedNotebooksSectionContent(
undefined,
"You have successfully published the following notebook(s) to public gallery and shared with other Azure Cosmos DB users.",
this.createCardsTabContent(published)
)}
{underReview?.length > 0 &&
this.createPublishedNotebooksSectionContent(
"Under Review",
"Content of a notebook you published is currently being scanned for illegal content. It will not be available to public gallery until the review is completed (may take a few days)",
this.createCardsTabContent(underReview)
)}
{removed?.length > 0 &&
this.createPublishedNotebooksSectionContent(
"Removed",
"These notebooks were found to contain illegal content and has been taken down.",
this.createPolicyViolationsListContent(removed)
)}
</Stack>
);
return this.createSearchBarHeader(content);
};
private createPublishedNotebooksSectionContent = (
title: string,
description: string,
content: JSX.Element
): JSX.Element => {
return (
<Stack tokens={{ childrenGap: 5 }}>
{title && <Text styles={{ root: { fontWeight: FontWeights.semibold } }}>{title}</Text>}
{description && <Text>{description}</Text>}
{content}
</Stack>
);
};
private createPublicGalleryTabContent(data: IGalleryItem[], acceptedCodeOfConduct: boolean): JSX.Element {
return acceptedCodeOfConduct === false ? (
<CodeOfConductComponent
@@ -261,11 +210,11 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
}}
/>
) : (
this.createSearchBarHeader(this.createCardsTabContent(data))
this.createTabContent(data)
);
}
private createSearchBarHeader(content: JSX.Element): JSX.Element {
private createTabContent(data: IGalleryItem[]): JSX.Element {
return (
<Stack tokens={{ childrenGap: 10 }}>
<Stack horizontal tokens={{ childrenGap: 20, padding: 10 }}>
@@ -278,13 +227,13 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
<Stack.Item styles={{ root: { minWidth: 200 } }}>
<Dropdown options={this.sortingOptions} selectedKey={this.state.sortBy} onChange={this.onDropdownChange} />
</Stack.Item>
{(!this.props.container || this.props.container.isGalleryPublishEnabled()) && (
{this.props.container?.isGalleryPublishEnabled() && (
<Stack.Item>
<InfoComponent />
</Stack.Item>
)}
</Stack>
<Stack.Item>{content}</Stack.Item>
{data && this.createCardsTabContent(data)}
</Stack>
);
}
@@ -302,25 +251,6 @@ export class GalleryViewerComponent extends React.Component<GalleryViewerCompone
);
}
private createPolicyViolationsListContent(data: IGalleryItem[]): JSX.Element {
return (
<table>
<tbody>
<tr>
<th>Name</th>
<th>Policy violations</th>
</tr>
{data.map(item => (
<tr key={`policy-violations-tr-${item.id}`}>
<td>{item.name}</td>
<td>{item.policyViolations.join(", ")}</td>
</tr>
))}
</tbody>
</table>
);
}
private loadTabContent(tab: GalleryTab, searchText: string, sortBy: SortBy, offline: boolean): void {
switch (tab) {
case GalleryTab.OfficialSamples:

View File

@@ -3,14 +3,10 @@ import { Icon, Label, Stack, HoverCard, HoverCardType, Link } from "office-ui-fa
import { CodeOfConductEndpoints } from "../../../../Common/Constants";
import "./InfoComponent.less";
export interface InfoComponentProps {
onReportAbuseClick?: () => void;
}
export class InfoComponent extends React.Component<InfoComponentProps> {
private getInfoPanel = (iconName: string, labelText: string, url?: string, onClick?: () => void): JSX.Element => {
export class InfoComponent extends React.Component {
private getInfoPanel = (iconName: string, labelText: string, url: string): JSX.Element => {
return (
<Link href={url} target={url && "_blank"} onClick={onClick}>
<Link href={url} target="_blank">
<div className="infoPanel">
<Icon iconName={iconName} styles={{ root: { verticalAlign: "middle" } }} />
<Label className="infoLabel">{labelText}</Label>
@@ -29,11 +25,6 @@ export class InfoComponent extends React.Component<InfoComponentProps> {
<Stack.Item>
{this.getInfoPanel("KnowledgeArticle", "Microsoft Terms of Use", CodeOfConductEndpoints.termsOfUse)}
</Stack.Item>
{this.props.onReportAbuseClick && (
<Stack.Item>
{this.getInfoPanel("ReportHacked", "Report Abuse", undefined, () => this.props.onReportAbuseClick())}
</Stack.Item>
)}
</Stack>
);
};

View File

@@ -13,7 +13,7 @@ exports[`InfoComponent renders 1`] = `
<div
className="infoPanelMain"
>
<StyledIconBase
<Memo(StyledIconBase)
className="infoIconMain"
iconName="Help"
styles={

View File

@@ -77,25 +77,7 @@ exports[`GalleryViewerComponent renders 1`] = `
selectedKey={0}
/>
</StackItem>
<StackItem>
<InfoComponent />
</StackItem>
</Stack>
<StackItem>
<FocusZone
direction={2}
isCircularNavigation={false}
shouldRaiseClicks={true}
>
<List
getPageSpecification={[Function]}
onRenderCell={[Function]}
renderedWindowsAhead={3}
renderedWindowsBehind={2}
startIndex={0}
/>
</FocusZone>
</StackItem>
</Stack>
</PivotItem>
</StyledPivotBase>

View File

@@ -18,17 +18,14 @@ describe("NotebookMetadataComponent", () => {
downloads: 0,
favorites: 0,
views: 0,
newCellId: undefined,
policyViolations: undefined,
pendingScanJobIds: undefined
newCellId: undefined
},
isFavorite: false,
downloadButtonText: "Download",
onTagClick: undefined,
onDownloadClick: undefined,
onFavoriteClick: undefined,
onUnfavoriteClick: undefined,
onReportAbuseClick: undefined
onUnfavoriteClick: undefined
};
const wrapper = shallow(<NotebookMetadataComponent {...props} />);
@@ -50,17 +47,14 @@ describe("NotebookMetadataComponent", () => {
downloads: 0,
favorites: 0,
views: 0,
newCellId: undefined,
policyViolations: undefined,
pendingScanJobIds: undefined
newCellId: undefined
},
isFavorite: true,
downloadButtonText: "Download",
onTagClick: undefined,
onDownloadClick: undefined,
onFavoriteClick: undefined,
onUnfavoriteClick: undefined,
onReportAbuseClick: undefined
onUnfavoriteClick: undefined
};
const wrapper = shallow(<NotebookMetadataComponent {...props} />);

View File

@@ -17,7 +17,6 @@ import { IGalleryItem } from "../../../Juno/JunoClient";
import { FileSystemUtil } from "../../Notebook/FileSystemUtil";
import "./NotebookViewerComponent.less";
import CosmosDBLogo from "../../../../images/CosmosDB-logo.svg";
import { InfoComponent } from "../NotebookGallery/InfoComponent/InfoComponent";
export interface NotebookMetadataComponentProps {
data: IGalleryItem;
@@ -27,7 +26,6 @@ export interface NotebookMetadataComponentProps {
onFavoriteClick: () => void;
onUnfavoriteClick: () => void;
onDownloadClick: () => void;
onReportAbuseClick: () => void;
}
export class NotebookMetadataComponent extends React.Component<NotebookMetadataComponentProps> {
@@ -43,39 +41,24 @@ export class NotebookMetadataComponent extends React.Component<NotebookMetadataC
return (
<Stack tokens={{ childrenGap: 10 }}>
<Stack horizontal verticalAlign="center" tokens={{ childrenGap: 30 }}>
<Stack.Item>
<Text variant="xxLarge" nowrap>
{FileSystemUtil.stripExtension(this.props.data.name, "ipynb")}
</Text>
</Stack.Item>
<Stack.Item>
<Text>
{this.props.isFavorite !== undefined && (
<>
<IconButton
iconProps={{ iconName: this.props.isFavorite ? "HeartFill" : "Heart" }}
onClick={this.props.isFavorite ? this.props.onUnfavoriteClick : this.props.onFavoriteClick}
/>
{this.props.data.favorites} likes
</>
)}
</Text>
</Stack.Item>
<Text variant="xxLarge" nowrap>
{FileSystemUtil.stripExtension(this.props.data.name, "ipynb")}
</Text>
<Text>
{this.props.isFavorite !== undefined && (
<>
<IconButton
iconProps={{ iconName: this.props.isFavorite ? "HeartFill" : "Heart" }}
onClick={this.props.isFavorite ? this.props.onUnfavoriteClick : this.props.onFavoriteClick}
/>
{this.props.data.favorites} likes
</>
)}
</Text>
{this.props.downloadButtonText && (
<Stack.Item>
<PrimaryButton text={this.props.downloadButtonText} onClick={this.props.onDownloadClick} />
</Stack.Item>
<PrimaryButton text={this.props.downloadButtonText} onClick={this.props.onDownloadClick} />
)}
<Stack.Item grow>
<></>
</Stack.Item>
<Stack.Item>
<InfoComponent onReportAbuseClick={this.props.onReportAbuseClick} />
</Stack.Item>
</Stack>
<Stack horizontal verticalAlign="center" tokens={{ childrenGap: 10 }}>

View File

@@ -3,10 +3,11 @@
*/
import { Notebook } from "@nteract/commutable";
import { createContentRef } from "@nteract/core";
import { IChoiceGroupProps, Icon, Link, ProgressIndicator } from "office-ui-fabric-react";
import { Icon, Link, ProgressIndicator } from "office-ui-fabric-react";
import * as React from "react";
import { contents } from "rx-jupyter";
import * as Logger from "../../../Common/Logger";
import * as ViewModels from "../../../Contracts/ViewModels";
import { IGalleryItem, JunoClient } from "../../../Juno/JunoClient";
import * as GalleryUtils from "../../../Utils/GalleryUtils";
import * as NotificationConsoleUtils from "../../../Utils/NotificationConsoleUtils";
@@ -14,13 +15,12 @@ import { ConsoleDataType } from "../../Menus/NotificationConsole/NotificationCon
import { NotebookClientV2 } from "../../Notebook/NotebookClientV2";
import { NotebookComponentBootstrapper } from "../../Notebook/NotebookComponent/NotebookComponentBootstrapper";
import NotebookReadOnlyRenderer from "../../Notebook/NotebookRenderer/NotebookReadOnlyRenderer";
import { DialogComponent, DialogProps, TextFieldProps } from "../DialogReactComponent/DialogComponent";
import { DialogComponent, DialogProps } from "../DialogReactComponent/DialogComponent";
import { NotebookMetadataComponent } from "./NotebookMetadataComponent";
import "./NotebookViewerComponent.less";
import Explorer from "../../Explorer";
import { NotebookV4 } from "@nteract/commutable/lib/v4";
import { SessionStorageUtility } from "../../../Shared/StorageUtility";
import { DialogHost } from "../../../Utils/GalleryUtils";
export interface NotebookViewerComponentProps {
container?: Explorer;
@@ -43,8 +43,10 @@ interface NotebookViewerComponentState {
showProgressBar: boolean;
}
export class NotebookViewerComponent extends React.Component<NotebookViewerComponentProps, NotebookViewerComponentState>
implements DialogHost {
export class NotebookViewerComponent extends React.Component<
NotebookViewerComponentProps,
NotebookViewerComponentState
> {
private clientManager: NotebookClientV2;
private notebookComponentBootstrapper: NotebookComponentBootstrapper;
@@ -56,7 +58,7 @@ export class NotebookViewerComponent extends React.Component<NotebookViewerCompo
databaseAccountName: undefined,
defaultExperience: "NotebookViewer",
isReadOnly: true,
cellEditorType: "monaco",
cellEditorType: "codemirror",
autoSaveInterval: 365 * 24 * 3600 * 1000, // There is no way to turn off auto-save, set to 1 year
contentProvider: contents.JupyterContentProvider // NotebookViewer only knows how to talk to Jupyter contents API
});
@@ -138,7 +140,6 @@ export class NotebookViewerComponent extends React.Component<NotebookViewerCompo
onFavoriteClick={this.favoriteItem}
onUnfavoriteClick={this.unfavoriteItem}
onDownloadClick={this.downloadItem}
onReportAbuseClick={this.state.galleryItem.isSample ? undefined : this.reportAbuse}
/>
</div>
) : (
@@ -178,39 +179,6 @@ export class NotebookViewerComponent extends React.Component<NotebookViewerCompo
};
}
// DialogHost
showOkCancelModalDialog(
title: string,
msg: string,
okLabel: string,
onOk: () => void,
cancelLabel: string,
onCancel: () => void,
choiceGroupProps?: IChoiceGroupProps,
textFieldProps?: TextFieldProps
): void {
this.setState({
dialogProps: {
isModal: true,
visible: true,
title,
subText: msg,
primaryButtonText: okLabel,
secondaryButtonText: cancelLabel,
onPrimaryButtonClick: () => {
this.setState({ dialogProps: undefined });
onOk && onOk();
},
onSecondaryButtonClick: () => {
this.setState({ dialogProps: undefined });
onCancel && onCancel();
},
choiceGroupProps,
textFieldProps
}
});
}
private favoriteItem = async (): Promise<void> => {
GalleryUtils.favoriteItem(this.props.container, this.props.junoClient, this.state.galleryItem, item =>
this.setState({ galleryItem: item, isFavorite: true })
@@ -228,8 +196,4 @@ export class NotebookViewerComponent extends React.Component<NotebookViewerCompo
this.setState({ galleryItem: item })
);
};
private reportAbuse = (): void => {
GalleryUtils.reportAbuse(this.props.junoClient, this.state.galleryItem, this, () => {});
};
}

View File

@@ -17,38 +17,26 @@ exports[`NotebookMetadataComponent renders liked notebook 1`] = `
}
verticalAlign="center"
>
<StackItem>
<Text
nowrap={true}
variant="xxLarge"
>
name
</Text>
</StackItem>
<StackItem>
<Text>
<CustomizedIconButton
iconProps={
Object {
"iconName": "HeartFill",
}
<Text
nowrap={true}
variant="xxLarge"
>
name
</Text>
<Text>
<CustomizedIconButton
iconProps={
Object {
"iconName": "HeartFill",
}
/>
0
likes
</Text>
</StackItem>
<StackItem>
<CustomizedPrimaryButton
text="Download"
}
/>
</StackItem>
<StackItem
grow={true}
0
likes
</Text>
<CustomizedPrimaryButton
text="Download"
/>
<StackItem>
<InfoComponent />
</StackItem>
</Stack>
<Stack
horizontal={true}
@@ -68,14 +56,14 @@ exports[`NotebookMetadataComponent renders liked notebook 1`] = `
Invalid Date
</Text>
<Text>
<StyledIconBase
<Memo(StyledIconBase)
iconName="RedEye"
/>
0
</Text>
<Text>
<StyledIconBase
<Memo(StyledIconBase)
iconName="Download"
/>
0
@@ -129,38 +117,26 @@ exports[`NotebookMetadataComponent renders un-liked notebook 1`] = `
}
verticalAlign="center"
>
<StackItem>
<Text
nowrap={true}
variant="xxLarge"
>
name
</Text>
</StackItem>
<StackItem>
<Text>
<CustomizedIconButton
iconProps={
Object {
"iconName": "Heart",
}
<Text
nowrap={true}
variant="xxLarge"
>
name
</Text>
<Text>
<CustomizedIconButton
iconProps={
Object {
"iconName": "Heart",
}
/>
0
likes
</Text>
</StackItem>
<StackItem>
<CustomizedPrimaryButton
text="Download"
}
/>
</StackItem>
<StackItem
grow={true}
0
likes
</Text>
<CustomizedPrimaryButton
text="Download"
/>
<StackItem>
<InfoComponent />
</StackItem>
</Stack>
<Stack
horizontal={true}
@@ -180,14 +156,14 @@ exports[`NotebookMetadataComponent renders un-liked notebook 1`] = `
Invalid Date
</Text>
<Text>
<StyledIconBase
<Memo(StyledIconBase)
iconName="RedEye"
/>
0
</Text>
<Text>
<StyledIconBase
<Memo(StyledIconBase)
iconName="Download"
/>
0

View File

@@ -6,7 +6,7 @@ import SettingsTabV2 from "../../Tabs/SettingsTabV2";
import { collection } from "./TestUtils";
import * as DataModels from "../../../Contracts/DataModels";
import ko from "knockout";
import { TtlType, isDirty } from "./SettingsUtils";
import { TtlType, isDirty, TtlOnNoDefault, TtlOn, TtlOff } from "./SettingsUtils";
import Explorer from "../../Explorer";
import { updateCollection } from "../../../Common/dataAccess/updateCollection";
jest.mock("../../../Common/dataAccess/updateCollection", () => ({
@@ -20,8 +20,8 @@ jest.mock("../../../Common/dataAccess/updateCollection", () => ({
geospatialConfig: undefined
} as DataModels.Collection)
}));
import { updateOffer } from "../../../Common/dataAccess/updateOffer";
jest.mock("../../../Common/dataAccess/updateOffer", () => ({
import { updateOffer } from "../../../Common/DocumentClientUtilityBase";
jest.mock("../../../Common/DocumentClientUtilityBase", () => ({
updateOffer: jest.fn().mockReturnValue({} as DataModels.Offer)
}));
@@ -33,6 +33,7 @@ describe("SettingsComponent", () => {
title: "Scale & Settings",
tabPath: "",
node: undefined,
selfLink: undefined,
hashLocation: "settings",
isActive: ko.observable(false),
onUpdateTabsButtons: undefined
@@ -102,7 +103,10 @@ describe("SettingsComponent", () => {
let settingsComponentInstance = new SettingsComponent(baseProps);
expect(settingsComponentInstance.shouldShowKeyspaceSharedThroughputMessage()).toEqual(false);
const newContainer = new Explorer();
const newContainer = new Explorer({
notificationsClient: undefined,
isEmulator: false
});
newContainer.isPreferredApiCassandra = ko.computed(() => true);
const newCollection = { ...collection };
@@ -143,7 +147,10 @@ describe("SettingsComponent", () => {
let settingsComponentInstance = new SettingsComponent(baseProps);
expect(settingsComponentInstance.hasConflictResolution()).toEqual(undefined);
const newContainer = new Explorer();
const newContainer = new Explorer({
notificationsClient: undefined,
isEmulator: false
});
newContainer.databaseAccount = ko.observable({
id: undefined,
name: undefined,
@@ -213,6 +220,13 @@ describe("SettingsComponent", () => {
expect(isDirty(state.throughput, state.throughputBaseline)).toEqual(false);
});
it("getTtlValue", async () => {
const settingsComponentInstance = new SettingsComponent(baseProps);
expect(settingsComponentInstance.getTtlValue(TtlType.OnNoDefault)).toEqual(TtlOnNoDefault);
expect(settingsComponentInstance.getTtlValue(TtlType.On)).toEqual(TtlOn);
expect(settingsComponentInstance.getTtlValue(TtlType.Off)).toEqual(TtlOff);
});
it("getAnalyticalStorageTtl", () => {
const newCollection = { ...collection };
newCollection.analyticalStorageTtl = ko.observable(10);

View File

@@ -6,11 +6,11 @@ import * as SharedConstants from "../../../Shared/Constants";
import * as ViewModels from "../../../Contracts/ViewModels";
import DiscardIcon from "../../../../images/discard.svg";
import SaveIcon from "../../../../images/save-cosmos.svg";
import { traceStart, traceFailure, traceSuccess, trace } from "../../../Shared/Telemetry/TelemetryProcessor";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import { traceStart, traceFailure, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor";
import { Action } from "../../../Shared/Telemetry/TelemetryConstants";
import { RequestOptions } from "@azure/cosmos/dist-esm";
import Explorer from "../../Explorer";
import { updateOffer } from "../../../Common/dataAccess/updateOffer";
import { updateOffer } from "../../../Common/DocumentClientUtilityBase";
import { updateCollection } from "../../../Common/dataAccess/updateCollection";
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
import { userContext } from "../../../UserContext";
@@ -27,6 +27,9 @@ import {
SettingsV2TabTypes,
getTabTitle,
isDirty,
TtlOff,
TtlOn,
TtlOnNoDefault,
parseConflictResolutionMode,
parseConflictResolutionProcedure
} from "./SettingsUtils";
@@ -35,7 +38,7 @@ import {
ConflictResolutionComponentProps
} from "./SettingsSubComponents/ConflictResolutionComponent";
import { SubSettingsComponent, SubSettingsComponentProps } from "./SettingsSubComponents/SubSettingsComponent";
import { Pivot, PivotItem, IPivotProps, IPivotItemProps } from "office-ui-fabric-react";
import { Pivot, PivotItem, IPivotProps, IPivotItemProps, IChoiceGroupOption } from "office-ui-fabric-react";
import "./SettingsComponent.less";
import { IndexingPolicyComponent, IndexingPolicyComponentProps } from "./SettingsSubComponents/IndexingPolicyComponent";
@@ -82,6 +85,7 @@ export interface SettingsComponentState {
indexingPolicyContent: DataModels.IndexingPolicy;
indexingPolicyContentBaseline: DataModels.IndexingPolicy;
shouldDiscardIndexingPolicy: boolean;
indexingPolicyElementFocussed: boolean;
isIndexingPolicyDirty: boolean;
conflictResolutionPolicyMode: DataModels.ConflictResolutionMode;
@@ -98,6 +102,7 @@ export interface SettingsComponentState {
export class SettingsComponent extends React.Component<SettingsComponentProps, SettingsComponentState> {
private static readonly sixMonthsInSeconds = 15768000;
private static readonly zeroSeconds = 0;
public saveSettingsButton: ButtonV2;
public discardSettingsChangesButton: ButtonV2;
@@ -122,8 +127,8 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
this.changeFeedPolicyVisible = this.collection?.container.isFeatureEnabled(
Constants.Features.enableChangeFeedPolicy
);
// Mongo container with system partition key still treat as "Fixed"
this.isFixedContainer =
!this.collection.partitionKey ||
(this.container.isPreferredApiMongoDB() && this.collection.partitionKey.systemKey);
@@ -155,6 +160,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
indexingPolicyContent: undefined,
indexingPolicyContentBaseline: undefined,
indexingPolicyElementFocussed: false,
shouldDiscardIndexingPolicy: false,
isIndexingPolicyDirty: false,
@@ -264,7 +270,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
this.props.settingsTab.isExecutionError(false);
this.props.settingsTab.isExecuting(true);
const startKey: number = traceStart(Action.SettingsV2Updated, {
const startKey: number = traceStart(Action.UpdateSettings, {
databaseAccountName: this.container.databaseAccount()?.name,
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
@@ -405,11 +411,11 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
this.setState({ isScaleSaveable: false, isScaleDiscardable: false });
} catch (error) {
traceFailure(
Action.SettingsV2Updated,
Action.UpdateSettings,
{
databaseAccountName: this.container.databaseAccount().name,
databaseName: this.collection?.databaseId,
collectionName: this.collection?.id(),
databaseName: this.collection && this.collection.databaseId,
collectionName: this.collection && this.collection.id(),
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.props.settingsTab.tabTitle(),
@@ -420,21 +426,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
throw error;
}
} else {
const updateOfferParams: DataModels.UpdateOfferParams = {
databaseId: this.collection.databaseId,
collectionId: this.collection.id(),
currentOffer: this.collection.offer(),
autopilotThroughput: this.state.isAutoPilotSelected ? this.state.autoPilotThroughput : undefined,
manualThroughput: this.state.isAutoPilotSelected ? undefined : newThroughput
};
if (this.hasProvisioningTypeChanged()) {
if (this.state.isAutoPilotSelected) {
updateOfferParams.migrateToAutoPilot = true;
} else {
updateOfferParams.migrateToManual = true;
}
}
const updatedOffer: DataModels.Offer = await updateOffer(updateOfferParams);
const updatedOffer: DataModels.Offer = await updateOffer(this.collection.offer(), newOffer, headerOptions);
this.collection.offer(updatedOffer);
this.setState({ isScaleSaveable: false, isScaleDiscardable: false });
if (this.state.isAutoPilotSelected) {
@@ -454,11 +446,9 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
this.setBaseline();
this.setState({ wasAutopilotOriginallySet: this.state.isAutoPilotSelected });
traceSuccess(
Action.SettingsV2Updated,
Action.UpdateSettings,
{
databaseAccountName: this.container.databaseAccount()?.name,
databaseName: this.collection?.databaseId,
collectionName: this.collection?.id(),
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.props.settingsTab.tabTitle()
@@ -470,15 +460,12 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
this.props.settingsTab.isExecutionError(true);
console.error(reason);
traceFailure(
Action.SettingsV2Updated,
Action.UpdateSettings,
{
databaseAccountName: this.container.databaseAccount()?.name,
databaseName: this.collection?.databaseId,
collectionName: this.collection?.id(),
defaultExperience: this.container.defaultExperience(),
dataExplorerArea: Constants.Areas.Tab,
tabTitle: this.props.settingsTab.tabTitle(),
error: reason
tabTitle: this.props.settingsTab.tabTitle()
},
startKey
);
@@ -487,10 +474,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
};
public onRevertClick = (): void => {
trace(Action.SettingsV2Discarded, ActionModifiers.Mark, {
message: "Settings Discarded"
});
this.setState({
throughput: this.state.throughputBaseline,
timeToLive: this.state.timeToLiveBaseline,
@@ -521,6 +504,9 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
private onScaleDiscardableChange = (isScaleDiscardable: boolean): void =>
this.setState({ isScaleDiscardable: isScaleDiscardable });
private onIndexingPolicyElementFocusChange = (indexingPolicyElementFocussed: boolean): void =>
this.setState({ indexingPolicyElementFocussed: indexingPolicyElementFocussed });
private onIndexingPolicyContentChange = (newIndexingPolicy: DataModels.IndexingPolicy): void =>
this.setState({ indexingPolicyContent: newIndexingPolicy });
@@ -544,34 +530,79 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
}
};
private onConflictResolutionPolicyModeChange = (newMode: DataModels.ConflictResolutionMode): void =>
this.setState({ conflictResolutionPolicyMode: newMode });
private onConflictResolutionPolicyModeChange = (
event?: React.FormEvent<HTMLElement | HTMLInputElement>,
option?: IChoiceGroupOption
): void =>
this.setState({
conflictResolutionPolicyMode:
DataModels.ConflictResolutionMode[option.key as keyof typeof DataModels.ConflictResolutionMode]
});
private onConflictResolutionPolicyPathChange = (newPath: string): void =>
this.setState({ conflictResolutionPolicyPath: newPath });
private onConflictResolutionPolicyPathChange = (
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
newValue?: string
): void => this.setState({ conflictResolutionPolicyPath: newValue });
private onConflictResolutionPolicyProcedureChange = (newProcedure: string): void =>
this.setState({ conflictResolutionPolicyProcedure: newProcedure });
private onConflictResolutionPolicyProcedureChange = (
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
newValue?: string
): void => this.setState({ conflictResolutionPolicyProcedure: newValue });
private onConflictResolutionDirtyChange = (isConflictResolutionDirty: boolean): void =>
this.setState({ isConflictResolutionDirty: isConflictResolutionDirty });
private onTtlChange = (newTtl: TtlType): void => this.setState({ timeToLive: newTtl });
public getTtlValue = (value: string): TtlType => {
switch (value) {
case TtlOn:
return TtlType.On;
case TtlOff:
return TtlType.Off;
case TtlOnNoDefault:
return TtlType.OnNoDefault;
}
return undefined;
};
private onTimeToLiveSecondsChange = (newTimeToLiveSeconds: number): void =>
private onTtlChange = (ev?: React.FormEvent<HTMLElement | HTMLInputElement>, option?: IChoiceGroupOption): void =>
this.setState({ timeToLive: this.getTtlValue(option.key) });
private onTimeToLiveSecondsChange = (
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
newValue?: string
): void => {
let newTimeToLiveSeconds = parseInt(newValue);
newTimeToLiveSeconds = isNaN(newTimeToLiveSeconds) ? SettingsComponent.zeroSeconds : newTimeToLiveSeconds;
this.setState({ timeToLiveSeconds: newTimeToLiveSeconds });
};
private onGeoSpatialConfigTypeChange = (newGeoSpatialConfigType: GeospatialConfigType): void =>
this.setState({ geospatialConfigType: newGeoSpatialConfigType });
private onGeoSpatialConfigTypeChange = (
ev?: React.FormEvent<HTMLElement | HTMLInputElement>,
option?: IChoiceGroupOption
): void =>
this.setState({ geospatialConfigType: GeospatialConfigType[option.key as keyof typeof GeospatialConfigType] });
private onAnalyticalStorageTtlSelectionChange = (newAnalyticalStorageTtlSelection: TtlType): void =>
this.setState({ analyticalStorageTtlSelection: newAnalyticalStorageTtlSelection });
private onAnalyticalStorageTtlSelectionChange = (
ev?: React.FormEvent<HTMLElement | HTMLInputElement>,
option?: IChoiceGroupOption
): void => this.setState({ analyticalStorageTtlSelection: this.getTtlValue(option.key) });
private onAnalyticalStorageTtlSecondsChange = (newAnalyticalStorageTtlSeconds: number): void =>
private onAnalyticalStorageTtlSecondsChange = (
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
newValue?: string
): void => {
let newAnalyticalStorageTtlSeconds = parseInt(newValue);
newAnalyticalStorageTtlSeconds = isNaN(newAnalyticalStorageTtlSeconds)
? SettingsComponent.zeroSeconds
: newAnalyticalStorageTtlSeconds;
this.setState({ analyticalStorageTtlSeconds: newAnalyticalStorageTtlSeconds });
};
private onChangeFeedPolicyChange = (newChangeFeedPolicy: ChangeFeedPolicyState): void =>
this.setState({ changeFeedPolicy: newChangeFeedPolicy });
private onChangeFeedPolicyChange = (
ev?: React.FormEvent<HTMLElement | HTMLInputElement>,
option?: IChoiceGroupOption
): void =>
this.setState({ changeFeedPolicy: ChangeFeedPolicyState[option.key as keyof typeof ChangeFeedPolicyState] });
private onSubSettingsSaveableChange = (isSubSettingsSaveable: boolean): void =>
this.setState({ isSubSettingsSaveable: isSubSettingsSaveable });
@@ -799,6 +830,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
resetShouldDiscardIndexingPolicy: this.resetShouldDiscardIndexingPolicy,
indexingPolicyContent: this.state.indexingPolicyContent,
indexingPolicyContentBaseline: this.state.indexingPolicyContentBaseline,
onIndexingPolicyElementFocusChange: this.onIndexingPolicyElementFocusChange,
onIndexingPolicyContentChange: this.onIndexingPolicyContentChange,
logIndexingPolicySuccessMessage: this.logIndexingPolicySuccessMessage,
onIndexingPolicyDirtyChange: this.onIndexingPolicyDirtyChange

View File

@@ -18,15 +18,24 @@ export interface ConflictResolutionComponentProps {
container: Explorer;
conflictResolutionPolicyMode: DataModels.ConflictResolutionMode;
conflictResolutionPolicyModeBaseline: DataModels.ConflictResolutionMode;
onConflictResolutionPolicyModeChange: (newMode: DataModels.ConflictResolutionMode) => void;
onConflictResolutionPolicyModeChange: (
event?: React.FormEvent<HTMLElement | HTMLInputElement>,
option?: IChoiceGroupOption
) => void;
conflictResolutionPolicyPath: string;
conflictResolutionPolicyPathBaseline: string;
onConflictResolutionPolicyPathChange: (newPath: string) => void;
onConflictResolutionPolicyPathChange: (
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
newValue?: string
) => void;
conflictResolutionPolicyProcedure: string;
conflictResolutionPolicyProcedureBaseline: string;
onConflictResolutionPolicyProcedureChange: (newProcedure: string) => void;
onConflictResolutionPolicyProcedureChange: (
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
newValue?: string
) => void;
onConflictResolutionDirtyChange: (isConflictResolutionDirty: boolean) => void;
}
@@ -68,30 +77,12 @@ export class ConflictResolutionComponent extends React.Component<ConflictResolut
return false;
};
private onConflictResolutionPolicyModeChange = (
event?: React.FormEvent<HTMLElement | HTMLInputElement>,
option?: IChoiceGroupOption
): void =>
this.props.onConflictResolutionPolicyModeChange(
DataModels.ConflictResolutionMode[option.key as keyof typeof DataModels.ConflictResolutionMode]
);
private onConflictResolutionPolicyPathChange = (
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
newValue?: string
): void => this.props.onConflictResolutionPolicyPathChange(newValue);
private onConflictResolutionPolicyProcedureChange = (
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
newValue?: string
): void => this.props.onConflictResolutionPolicyProcedureChange(newValue);
private getConflictResolutionModeComponent = (): JSX.Element => (
<ChoiceGroup
label="Mode"
selectedKey={this.props.conflictResolutionPolicyMode}
options={this.conflictResolutionChoiceGroupOptions}
onChange={this.onConflictResolutionPolicyModeChange}
onChange={this.props.onConflictResolutionPolicyModeChange}
styles={getChoiceGroupStyles(
this.props.conflictResolutionPolicyMode,
this.props.conflictResolutionPolicyModeBaseline
@@ -113,7 +104,7 @@ export class ConflictResolutionComponent extends React.Component<ConflictResolut
this.props.conflictResolutionPolicyPathBaseline
)}
value={this.props.conflictResolutionPolicyPath}
onChange={this.onConflictResolutionPolicyPathChange}
onChange={this.props.onConflictResolutionPolicyPathChange}
/>
);
@@ -131,7 +122,7 @@ export class ConflictResolutionComponent extends React.Component<ConflictResolut
this.props.conflictResolutionPolicyProcedureBaseline
)}
value={this.props.conflictResolutionPolicyProcedure}
onChange={this.onConflictResolutionPolicyProcedureChange}
onChange={this.props.onConflictResolutionPolicyProcedureChange}
/>
);

View File

@@ -17,6 +17,9 @@ describe("IndexingPolicyComponent", () => {
},
indexingPolicyContent: initialIndexingPolicyContent,
indexingPolicyContentBaseline: initialIndexingPolicyContent,
onIndexingPolicyElementFocusChange: () => {
return;
},
onIndexingPolicyContentChange: () => {
return;
},

View File

@@ -10,6 +10,7 @@ export interface IndexingPolicyComponentProps {
resetShouldDiscardIndexingPolicy: () => void;
indexingPolicyContent: DataModels.IndexingPolicy;
indexingPolicyContentBaseline: DataModels.IndexingPolicy;
onIndexingPolicyElementFocusChange: (indexingPolicyContentFocussed: boolean) => void;
onIndexingPolicyContentChange: (newIndexingPolicy: DataModels.IndexingPolicy) => void;
logIndexingPolicySuccessMessage: () => void;
onIndexingPolicyDirtyChange: (isIndexingPolicyDirty: boolean) => void;
@@ -88,6 +89,8 @@ export class IndexingPolicyComponent extends React.Component<
ariaLabel: "Indexing Policy"
});
if (this.indexingPolicyEditor) {
this.indexingPolicyEditor.onDidFocusEditorText(() => this.props.onIndexingPolicyElementFocusChange(true));
this.indexingPolicyEditor.onDidBlurEditorText(() => this.props.onIndexingPolicyElementFocusChange(false));
const indexingPolicyEditorModel = this.indexingPolicyEditor.getModel();
indexingPolicyEditorModel.onDidChangeContent(this.onEditorContentChange.bind(this));
this.props.logIndexingPolicySuccessMessage();

View File

@@ -5,13 +5,18 @@ import { container, collection } from "../TestUtils";
import { ThroughputInputAutoPilotV3Component } from "./ThroughputInputComponents/ThroughputInputAutoPilotV3Component";
import Explorer from "../../../Explorer";
import * as Constants from "../../../../Common/Constants";
import { PlatformType } from "../../../../PlatformType";
import * as DataModels from "../../../../Contracts/DataModels";
import { throughputUnit } from "../SettingsRenderUtils";
import * as SharedConstants from "../../../../Shared/Constants";
import ko from "knockout";
describe("ScaleComponent", () => {
const nonNationalCloudContainer = new Explorer();
const nonNationalCloudContainer = new Explorer({
notificationsClient: undefined,
isEmulator: false
});
nonNationalCloudContainer.getPlatformType = () => PlatformType.Portal;
nonNationalCloudContainer.isRunningOnNationalCloud = () => false;
const targetThroughput = 6000;
@@ -83,7 +88,10 @@ describe("ScaleComponent", () => {
});
it("autoScale enabled", () => {
const newContainer = new Explorer();
const newContainer = new Explorer({
notificationsClient: undefined,
isEmulator: false
});
newContainer.databaseAccount({
id: undefined,
@@ -117,7 +125,7 @@ describe("ScaleComponent", () => {
it("getThroughputTitle", () => {
let scaleComponent = new ScaleComponent(baseProps);
expect(scaleComponent.getThroughputTitle()).toEqual("Throughput (6,000 - unlimited RU/s)");
expect(scaleComponent.getThroughputTitle()).toEqual("Throughput (6,000 - 40,000 RU/s)");
let newProps = { ...baseProps, container: nonNationalCloudContainer };
scaleComponent = new ScaleComponent(newProps);
@@ -130,7 +138,7 @@ describe("ScaleComponent", () => {
it("canThroughputExceedMaximumValue", () => {
let scaleComponent = new ScaleComponent(baseProps);
expect(scaleComponent.canThroughputExceedMaximumValue()).toEqual(true);
expect(scaleComponent.canThroughputExceedMaximumValue()).toEqual(false);
const newProps = { ...baseProps, container: nonNationalCloudContainer };
scaleComponent = new ScaleComponent(newProps);

View File

@@ -5,6 +5,7 @@ import * as ViewModels from "../../../../Contracts/ViewModels";
import * as DataModels from "../../../../Contracts/DataModels";
import * as SharedConstants from "../../../../Shared/Constants";
import Explorer from "../../../Explorer";
import { PlatformType } from "../../../../PlatformType";
import {
getTextFieldStyles,
subComponentStackProps,
@@ -18,7 +19,6 @@ import {
import { getMaxRUs, getMinRUs, hasDatabaseSharedThroughput } from "../SettingsUtils";
import * as AutoPilotUtils from "../../../../Utils/AutoPilotUtils";
import { Text, TextField, Stack, Label, MessageBar, MessageBarType } from "office-ui-fabric-react";
import { configContext, Platform } from "../../../../ConfigContext";
export interface ScaleComponentProps {
collection: ViewModels.Collection;
@@ -43,7 +43,7 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
private isEmulator: boolean;
constructor(props: ScaleComponentProps) {
super(props);
this.isEmulator = configContext.platform === Platform.Emulator;
this.isEmulator = this.props.container.isEmulator;
}
public isAutoScaleEnabled = (): boolean => {
@@ -77,7 +77,7 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
};
public getMaxRUThroughputInputLimit = (): number => {
if (configContext.platform === Platform.Hosted && this.props.collection.partitionKey) {
if (this.props.container?.getPlatformType() === PlatformType.Hosted && this.props.collection.partitionKey) {
return SharedConstants.CollectionCreation.DefaultCollectionRUs1Million;
}
@@ -99,7 +99,8 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
public canThroughputExceedMaximumValue = (): boolean => {
const isPublicAzurePortal: boolean =
configContext.platform === Platform.Portal && !this.props.container.isRunningOnNationalCloud();
this.props.container.getPlatformType() === PlatformType.Portal &&
!this.props.container.isRunningOnNationalCloud();
const hasPartitionKey = !!this.props.collection.partitionKey;
return isPublicAzurePortal && hasPartitionKey;

View File

@@ -2,7 +2,7 @@ import { shallow } from "enzyme";
import React from "react";
import { SubSettingsComponent, SubSettingsComponentProps } from "./SubSettingsComponent";
import { container, collection } from "../TestUtils";
import { TtlType, GeospatialConfigType, ChangeFeedPolicyState, TtlOnNoDefault, TtlOn, TtlOff } from "../SettingsUtils";
import { TtlType, GeospatialConfigType, ChangeFeedPolicyState } from "../SettingsUtils";
import ko from "knockout";
import Explorer from "../../../Explorer";
@@ -105,7 +105,10 @@ describe("SubSettingsComponent", () => {
});
it("partitionKey not visible", () => {
const newContainer = new Explorer();
const newContainer = new Explorer({
notificationsClient: undefined,
isEmulator: false
});
newContainer.isPreferredApiCassandra = ko.computed(() => true);
const props = { ...baseProps, container: newContainer };
@@ -130,11 +133,4 @@ describe("SubSettingsComponent", () => {
expect(isComponentDirtyResult.isSaveable).toEqual(true);
expect(isComponentDirtyResult.isDiscardable).toEqual(true);
});
it("getTtlValue", async () => {
const subSettingsComponentInstance = new SubSettingsComponent(baseProps);
expect(subSettingsComponentInstance.getTtlValue(TtlType.OnNoDefault)).toEqual(TtlOnNoDefault);
expect(subSettingsComponentInstance.getTtlValue(TtlType.On)).toEqual(TtlOn);
expect(subSettingsComponentInstance.getTtlValue(TtlType.Off)).toEqual(TtlOff);
});
});

View File

@@ -5,11 +5,7 @@ import {
TtlType,
ChangeFeedPolicyState,
isDirty,
IsComponentDirtyResult,
TtlOn,
TtlOff,
TtlOnNoDefault,
getSanitizedInputValue
IsComponentDirtyResult
} from "../SettingsUtils";
import Explorer from "../../../Explorer";
import { Int32 } from "../../../Panes/Tables/Validators/EntityPropertyValidationCommon";
@@ -41,28 +37,40 @@ export interface SubSettingsComponentProps {
timeToLive: TtlType;
timeToLiveBaseline: TtlType;
onTtlChange: (newTtl: TtlType) => void;
onTtlChange: (ev?: React.FormEvent<HTMLElement | HTMLInputElement>, option?: IChoiceGroupOption) => void;
timeToLiveSeconds: number;
timeToLiveSecondsBaseline: number;
onTimeToLiveSecondsChange: (newTimeToLiveSeconds: number) => void;
onTimeToLiveSecondsChange: (
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
newValue?: string
) => void;
geospatialConfigType: GeospatialConfigType;
geospatialConfigTypeBaseline: GeospatialConfigType;
onGeoSpatialConfigTypeChange: (newGeoSpatialConfigType: GeospatialConfigType) => void;
onGeoSpatialConfigTypeChange: (
ev?: React.FormEvent<HTMLElement | HTMLInputElement>,
option?: IChoiceGroupOption
) => void;
isAnalyticalStorageEnabled: boolean;
analyticalStorageTtlSelection: TtlType;
analyticalStorageTtlSelectionBaseline: TtlType;
onAnalyticalStorageTtlSelectionChange: (newAnalyticalStorageTtlSelection: TtlType) => void;
onAnalyticalStorageTtlSelectionChange: (
ev?: React.FormEvent<HTMLElement | HTMLInputElement>,
option?: IChoiceGroupOption
) => void;
analyticalStorageTtlSeconds: number;
analyticalStorageTtlSecondsBaseline: number;
onAnalyticalStorageTtlSecondsChange: (newAnalyticalStorageTtlSeconds: number) => void;
onAnalyticalStorageTtlSecondsChange: (
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
newValue?: string
) => void;
changeFeedPolicyVisible: boolean;
changeFeedPolicy: ChangeFeedPolicyState;
changeFeedPolicyBaseline: ChangeFeedPolicyState;
onChangeFeedPolicyChange: (newChangeFeedPolicyState: ChangeFeedPolicyState) => void;
onChangeFeedPolicyChange: (ev?: React.FormEvent<HTMLElement | HTMLInputElement>, option?: IChoiceGroupOption) => void;
onSubSettingsSaveableChange: (isSubSettingsSaveable: boolean) => void;
onSubSettingsDiscardableChange: (isSubSettingsDiscardable: boolean) => void;
}
@@ -131,54 +139,6 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
{ key: TtlType.On, text: "On" }
];
public getTtlValue = (value: string): TtlType => {
switch (value) {
case TtlOn:
return TtlType.On;
case TtlOff:
return TtlType.Off;
case TtlOnNoDefault:
return TtlType.OnNoDefault;
}
return undefined;
};
private onTtlChange = (ev?: React.FormEvent<HTMLElement | HTMLInputElement>, option?: IChoiceGroupOption): void =>
this.props.onTtlChange(this.getTtlValue(option.key));
private onTimeToLiveSecondsChange = (
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
newValue?: string
): void => {
const newTimeToLiveSeconds = getSanitizedInputValue(newValue, Int32.Max);
this.props.onTimeToLiveSecondsChange(newTimeToLiveSeconds);
};
private onGeoSpatialConfigTypeChange = (
ev?: React.FormEvent<HTMLElement | HTMLInputElement>,
option?: IChoiceGroupOption
): void =>
this.props.onGeoSpatialConfigTypeChange(GeospatialConfigType[option.key as keyof typeof GeospatialConfigType]);
private onAnalyticalStorageTtlSelectionChange = (
ev?: React.FormEvent<HTMLElement | HTMLInputElement>,
option?: IChoiceGroupOption
): void => this.props.onAnalyticalStorageTtlSelectionChange(this.getTtlValue(option.key));
private onAnalyticalStorageTtlSecondsChange = (
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
newValue?: string
): void => {
const newAnalyticalStorageTtlSeconds = getSanitizedInputValue(newValue, Int32.Max);
this.props.onAnalyticalStorageTtlSecondsChange(newAnalyticalStorageTtlSeconds);
};
private onChangeFeedPolicyChange = (
ev?: React.FormEvent<HTMLElement | HTMLInputElement>,
option?: IChoiceGroupOption
): void =>
this.props.onChangeFeedPolicyChange(ChangeFeedPolicyState[option.key as keyof typeof ChangeFeedPolicyState]);
private getTtlComponent = (): JSX.Element => (
<Stack {...titleAndInputStackProps}>
<ChoiceGroup
@@ -186,7 +146,7 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
label="Time to Live"
selectedKey={this.props.timeToLive}
options={this.ttlChoiceGroupOptions}
onChange={this.onTtlChange}
onChange={this.props.onTtlChange}
styles={getChoiceGroupStyles(this.props.timeToLive, this.props.timeToLiveBaseline)}
/>
{isDirty(this.props.timeToLive, this.props.timeToLiveBaseline) && this.props.timeToLive === TtlType.On && (
@@ -203,7 +163,7 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
min={1}
max={Int32.Max}
value={this.props.timeToLiveSeconds?.toString()}
onChange={this.onTimeToLiveSecondsChange}
onChange={this.props.onTimeToLiveSecondsChange}
suffix="second(s)"
/>
)}
@@ -223,7 +183,7 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
label="Analytical Storage Time to Live"
selectedKey={this.props.analyticalStorageTtlSelection}
options={this.analyticalTtlChoiceGroupOptions}
onChange={this.onAnalyticalStorageTtlSelectionChange}
onChange={this.props.onAnalyticalStorageTtlSelectionChange}
styles={getChoiceGroupStyles(
this.props.analyticalStorageTtlSelection,
this.props.analyticalStorageTtlSelectionBaseline
@@ -242,7 +202,7 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
max={Int32.Max}
value={this.props.analyticalStorageTtlSeconds?.toString()}
suffix="second(s)"
onChange={this.onAnalyticalStorageTtlSecondsChange}
onChange={this.props.onAnalyticalStorageTtlSecondsChange}
/>
)}
</Stack>
@@ -259,7 +219,7 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
label="Geospatial Configuration"
selectedKey={this.props.geospatialConfigType}
options={this.geoSpatialConfigTypeChoiceGroupOptions}
onChange={this.onGeoSpatialConfigTypeChange}
onChange={this.props.onGeoSpatialConfigTypeChange}
styles={getChoiceGroupStyles(this.props.geospatialConfigType, this.props.geospatialConfigTypeBaseline)}
/>
);
@@ -281,7 +241,7 @@ export class SubSettingsComponent extends React.Component<SubSettingsComponentPr
id="changeFeedPolicy"
selectedKey={this.props.changeFeedPolicy}
options={this.changeFeedChoiceGroupOptions}
onChange={this.onChangeFeedPolicyChange}
onChange={this.props.onChangeFeedPolicyChange}
styles={getChoiceGroupStyles(this.props.changeFeedPolicy, this.props.changeFeedPolicyBaseline)}
aria-labelledby={labelId}
/>

View File

@@ -26,10 +26,9 @@ import {
MessageBarType
} from "office-ui-fabric-react";
import { ToolTipLabelComponent } from "../ToolTipLabelComponent";
import { getSanitizedInputValue, IsComponentDirtyResult, isDirty } from "../../SettingsUtils";
import { IsComponentDirtyResult, isDirty } from "../../SettingsUtils";
import * as SharedConstants from "../../../../../Shared/Constants";
import * as DataModels from "../../../../../Contracts/DataModels";
import { Int32 } from "../../../../Panes/Tables/Validators/EntityPropertyValidationCommon";
export interface ThroughputInputAutoPilotV3Props {
databaseAccount: DataModels.DatabaseAccount;
@@ -72,9 +71,9 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
> {
private shouldCheckComponentIsDirty = true;
private static readonly defaultStep = 100;
private static readonly zeroThroughput = 0;
private step: number;
private throughputInputMaxValue: number;
private autoPilotInputMaxValue: number;
private choiceGroupFixedStyle = getChoiceGroupStyles(undefined, undefined);
private options: IChoiceGroupOption[] = [
{ key: "true", text: "Autoscale" },
{ key: "false", text: "Manual" }
@@ -141,8 +140,6 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
};
this.step = this.props.step ?? ThroughputInputAutoPilotV3Component.defaultStep;
this.throughputInputMaxValue = this.props.canExceedMaximumValue ? Int32.Max : this.props.maximum;
this.autoPilotInputMaxValue = Int32.Max;
}
public hasProvisioningTypeChanged = (): boolean =>
@@ -203,7 +200,8 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
newValue?: string
): void => {
const newThroughput = getSanitizedInputValue(newValue, this.autoPilotInputMaxValue);
let newThroughput = parseInt(newValue);
newThroughput = isNaN(newThroughput) ? ThroughputInputAutoPilotV3Component.zeroThroughput : newThroughput;
this.props.onMaxAutoPilotThroughputChange(newThroughput);
};
@@ -211,7 +209,9 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>,
newValue?: string
): void => {
const newThroughput = getSanitizedInputValue(newValue, this.throughputInputMaxValue);
let newThroughput = parseInt(newValue);
newThroughput = isNaN(newThroughput) ? ThroughputInputAutoPilotV3Component.zeroThroughput : newThroughput;
if (this.overrideWithAutoPilotSettings()) {
this.props.onMaxAutoPilotThroughputChange(newThroughput);
} else {
@@ -245,7 +245,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
onChange={this.onChoiceGroupChange}
required={this.props.showAsMandatory}
ariaLabelledBy={labelId}
styles={getChoiceGroupStyles(this.props.wasAutopilotOriginallySet, this.props.isAutoPilotSelected)}
styles={this.choiceGroupFixedStyle}
/>
</Stack>
);
@@ -270,7 +270,8 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
key="auto pilot throughput input"
styles={getTextFieldStyles(this.props.maxAutoPilotThroughput, this.props.maxAutoPilotThroughputBaseline)}
disabled={this.overrideWithProvisionedThroughputSettings()}
step={AutoPilotUtils.autoPilotIncrementStep}
step={this.step}
min={AutoPilotUtils.minAutoPilotThroughput}
value={this.overrideWithProvisionedThroughputSettings() ? "" : this.props.maxAutoPilotThroughput?.toString()}
onChange={this.onAutoPilotThroughputChange}
/>
@@ -297,6 +298,8 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
styles={getTextFieldStyles(this.props.throughput, this.props.throughputBaseline)}
disabled={this.overrideWithAutoPilotSettings()}
step={this.step}
min={this.props.minimum}
max={this.props.canExceedMaximumValue ? undefined : this.props.maximum}
value={
this.overrideWithAutoPilotSettings()
? this.props.maxAutoPilotThroughputBaseline?.toString()

View File

@@ -81,10 +81,10 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
Object {
"selectors": Object {
".ms-ChoiceField-field.is-checked::after": Object {
"borderColor": undefined,
"borderColor": "",
},
".ms-ChoiceField-field.is-checked::before": Object {
"borderColor": undefined,
"borderColor": "",
},
".ms-ChoiceField-wrapper label": Object {
"fontFamily": undefined,
@@ -113,9 +113,10 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
id="autopilotInput"
key="auto pilot throughput input"
label="Max RU/s"
min={4000}
onChange={[Function]}
required={true}
step={1000}
step={100}
styles={
Object {
"fieldGroup": Object {
@@ -218,6 +219,7 @@ exports[`ThroughputInputAutoPilotV3Component spendAck checkbox visible 1`] = `
disabled={false}
id="throughputInput"
key="provisioned throughput input"
min={10000}
onChange={[Function]}
required={true}
step={100}
@@ -373,6 +375,7 @@ exports[`ThroughputInputAutoPilotV3Component throughput input visible 1`] = `
disabled={false}
id="throughputInput"
key="provisioned throughput input"
min={10000}
onChange={[Function]}
required={true}
step={100}

View File

@@ -39,13 +39,13 @@ exports[`ScaleComponent renders with correct intiial notification 1`] = `
}
>
<ThroughputInputAutoPilotV3Component
canExceedMaximumValue={true}
canExceedMaximumValue={false}
getThroughputWarningMessage={[Function]}
isAutoPilotSelected={false}
isEmulator={false}
isEnabled={true}
isFixed={false}
label="Throughput (6,000 - unlimited RU/s)"
label="Throughput (6,000 - 40,000 RU/s)"
maxAutoPilotThroughput={4000}
maxAutoPilotThroughputBaseline={4000}
maximum={40000}

View File

@@ -41,7 +41,7 @@ exports[`ToolTipLabelComponent renders 1`] = `
}
}
>
<StyledIconBase
<Memo(StyledIconBase)
ariaLabel="Info"
iconName="Info"
styles={

View File

@@ -2,7 +2,6 @@ import { collection, container } from "./TestUtils";
import {
getMaxRUs,
getMinRUs,
getSanitizedInputValue,
hasDatabaseSharedThroughput,
isDirty,
isDirtyTypes,
@@ -87,11 +86,4 @@ describe("SettingsUtils", () => {
expect(isDirty(baseline, current)).toEqual(true);
});
});
it("getSanitizedInputValue", () => {
const max = 100;
expect(getSanitizedInputValue("", max)).toEqual(0);
expect(getSanitizedInputValue("999", max)).toEqual(99);
expect(getSanitizedInputValue("10", max)).toEqual(10);
});
});

View File

@@ -6,7 +6,6 @@ import * as PricingUtils from "../../../Utils/PricingUtils";
import Explorer from "../../Explorer";
const zeroValue = 0;
export type isDirtyTypes = boolean | string | number | DataModels.IndexingPolicy;
export const TtlOff = "off";
export const TtlOn = "on";
@@ -130,16 +129,6 @@ export const parseConflictResolutionProcedure = (procedureFromBackEnd: string):
return procedureFromBackEnd;
};
export const getSanitizedInputValue = (newValueString: string, max: number): number => {
let newValue = parseInt(newValueString);
if (isNaN(newValue)) {
newValue = zeroValue;
} else if (newValue > max) {
newValue = Math.floor(newValue / 10);
}
return newValue;
};
export const isDirty = (current: isDirtyTypes, baseline: isDirtyTypes): boolean => {
const currentType = typeof current;
const baselineType = typeof baseline;

View File

@@ -3,7 +3,10 @@ import * as ViewModels from "../../../Contracts/ViewModels";
import Explorer from "../../Explorer";
import ko from "knockout";
export const container = new Explorer();
export const container = new Explorer({
notificationsClient: undefined,
isEmulator: false
});
export const collection = ({
container: container,

View File

@@ -84,6 +84,9 @@ exports[`SettingsComponent renders 1`] = `
"visible": [Function],
},
AddCollectionPane {
"_databaseOffers": HashMap {
"container": Object {},
},
"_isSynapseLinkEnabled": [Function],
"autoPilotThroughput": [Function],
"autoPilotTiersList": [Function],
@@ -583,6 +586,9 @@ exports[`SettingsComponent renders 1`] = `
"_refreshSparkEnabledStateForAccount": [Function],
"_resetNotebookWorkspace": [Function],
"addCollectionPane": AddCollectionPane {
"_databaseOffers": HashMap {
"container": Object {},
},
"_isSynapseLinkEnabled": [Function],
"autoPilotThroughput": [Function],
"autoPilotTiersList": [Function],
@@ -947,6 +953,7 @@ exports[`SettingsComponent renders 1`] = `
"validPartitionKeyValue": [Function],
"visible": [Function],
},
"extensionEndpoint": [Function],
"features": [Function],
"flight": [Function],
"graphStylingPane": GraphStylingPane {
@@ -976,6 +983,7 @@ exports[`SettingsComponent renders 1`] = `
"isAuthWithResourceToken": [Function],
"isCodeOfConductEnabled": [Function],
"isCopyNotebookPaneEnabled": [Function],
"isEmulator": false,
"isEnableMongoCapabilityPresent": [Function],
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isGalleryPublishEnabled": [Function],
@@ -1051,6 +1059,7 @@ exports[`SettingsComponent renders 1`] = `
"parameters": [Function],
},
"notificationConsoleData": [Function],
"notificationsClient": undefined,
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"onSwitchToConnectionString": [Function],
@@ -1390,6 +1399,9 @@ exports[`SettingsComponent renders 1`] = `
"visible": [Function],
},
AddCollectionPane {
"_databaseOffers": HashMap {
"container": Object {},
},
"_isSynapseLinkEnabled": [Function],
"autoPilotThroughput": [Function],
"autoPilotTiersList": [Function],
@@ -1889,6 +1901,9 @@ exports[`SettingsComponent renders 1`] = `
"_refreshSparkEnabledStateForAccount": [Function],
"_resetNotebookWorkspace": [Function],
"addCollectionPane": AddCollectionPane {
"_databaseOffers": HashMap {
"container": Object {},
},
"_isSynapseLinkEnabled": [Function],
"autoPilotThroughput": [Function],
"autoPilotTiersList": [Function],
@@ -2253,6 +2268,7 @@ exports[`SettingsComponent renders 1`] = `
"validPartitionKeyValue": [Function],
"visible": [Function],
},
"extensionEndpoint": [Function],
"features": [Function],
"flight": [Function],
"graphStylingPane": GraphStylingPane {
@@ -2282,6 +2298,7 @@ exports[`SettingsComponent renders 1`] = `
"isAuthWithResourceToken": [Function],
"isCodeOfConductEnabled": [Function],
"isCopyNotebookPaneEnabled": [Function],
"isEmulator": false,
"isEnableMongoCapabilityPresent": [Function],
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isGalleryPublishEnabled": [Function],
@@ -2357,6 +2374,7 @@ exports[`SettingsComponent renders 1`] = `
"parameters": [Function],
},
"notificationConsoleData": [Function],
"notificationsClient": undefined,
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"onSwitchToConnectionString": [Function],
@@ -2709,6 +2727,9 @@ exports[`SettingsComponent renders 1`] = `
"visible": [Function],
},
AddCollectionPane {
"_databaseOffers": HashMap {
"container": Object {},
},
"_isSynapseLinkEnabled": [Function],
"autoPilotThroughput": [Function],
"autoPilotTiersList": [Function],
@@ -3208,6 +3229,9 @@ exports[`SettingsComponent renders 1`] = `
"_refreshSparkEnabledStateForAccount": [Function],
"_resetNotebookWorkspace": [Function],
"addCollectionPane": AddCollectionPane {
"_databaseOffers": HashMap {
"container": Object {},
},
"_isSynapseLinkEnabled": [Function],
"autoPilotThroughput": [Function],
"autoPilotTiersList": [Function],
@@ -3572,6 +3596,7 @@ exports[`SettingsComponent renders 1`] = `
"validPartitionKeyValue": [Function],
"visible": [Function],
},
"extensionEndpoint": [Function],
"features": [Function],
"flight": [Function],
"graphStylingPane": GraphStylingPane {
@@ -3601,6 +3626,7 @@ exports[`SettingsComponent renders 1`] = `
"isAuthWithResourceToken": [Function],
"isCodeOfConductEnabled": [Function],
"isCopyNotebookPaneEnabled": [Function],
"isEmulator": false,
"isEnableMongoCapabilityPresent": [Function],
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isGalleryPublishEnabled": [Function],
@@ -3676,6 +3702,7 @@ exports[`SettingsComponent renders 1`] = `
"parameters": [Function],
},
"notificationConsoleData": [Function],
"notificationsClient": undefined,
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"onSwitchToConnectionString": [Function],
@@ -4015,6 +4042,9 @@ exports[`SettingsComponent renders 1`] = `
"visible": [Function],
},
AddCollectionPane {
"_databaseOffers": HashMap {
"container": Object {},
},
"_isSynapseLinkEnabled": [Function],
"autoPilotThroughput": [Function],
"autoPilotTiersList": [Function],
@@ -4514,6 +4544,9 @@ exports[`SettingsComponent renders 1`] = `
"_refreshSparkEnabledStateForAccount": [Function],
"_resetNotebookWorkspace": [Function],
"addCollectionPane": AddCollectionPane {
"_databaseOffers": HashMap {
"container": Object {},
},
"_isSynapseLinkEnabled": [Function],
"autoPilotThroughput": [Function],
"autoPilotTiersList": [Function],
@@ -4878,6 +4911,7 @@ exports[`SettingsComponent renders 1`] = `
"validPartitionKeyValue": [Function],
"visible": [Function],
},
"extensionEndpoint": [Function],
"features": [Function],
"flight": [Function],
"graphStylingPane": GraphStylingPane {
@@ -4907,6 +4941,7 @@ exports[`SettingsComponent renders 1`] = `
"isAuthWithResourceToken": [Function],
"isCodeOfConductEnabled": [Function],
"isCopyNotebookPaneEnabled": [Function],
"isEmulator": false,
"isEnableMongoCapabilityPresent": [Function],
"isFixedCollectionWithSharedThroughputSupported": [Function],
"isGalleryPublishEnabled": [Function],
@@ -4982,6 +5017,7 @@ exports[`SettingsComponent renders 1`] = `
"parameters": [Function],
},
"notificationConsoleData": [Function],
"notificationsClient": undefined,
"onRefreshDatabasesKeyPress": [Function],
"onRefreshResourcesClick": [Function],
"onSwitchToConnectionString": [Function],
@@ -5293,6 +5329,7 @@ exports[`SettingsComponent renders 1`] = `
logIndexingPolicySuccessMessage={[Function]}
onIndexingPolicyContentChange={[Function]}
onIndexingPolicyDirtyChange={[Function]}
onIndexingPolicyElementFocusChange={[Function]}
resetShouldDiscardIndexingPolicy={[Function]}
shouldDiscardIndexingPolicy={false}
/>

View File

@@ -45,7 +45,7 @@
<input
class="throughputModeRadio nonFirstRadio"
aria-label="Manual mode"
aria-label="Provisioned Throughput mode"
type="radio"
role="radio"
tabindex="0"

View File

@@ -44,7 +44,7 @@
<input
class="throughputModeRadio nonFirstRadio"
aria-label="Manual mode"
aria-label="Provisioned Throughput mode"
type="radio"
role="radio"
tabindex="0"

View File

@@ -18,8 +18,6 @@ import {
import TriangleDownIcon from "../../../../images/Triangle-down.svg";
import TriangleRightIcon from "../../../../images/Triangle-right.svg";
import LoadingIndicator_3Squares from "../../../../images/LoadingIndicator_3Squares.gif";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
export interface TreeNodeMenuItem {
label: string;
@@ -278,12 +276,7 @@ export class TreeNodeComponent extends React.Component<TreeNodeComponentProps, T
text: menuItem.label,
disabled: menuItem.isDisabled,
className: menuItem.styleClass,
onClick: () => {
menuItem.onClick();
TelemetryProcessor.trace(Action.ClickResourceTreeNodeContextMenuItem, ActionModifiers.Mark, {
label: menuItem.label
});
},
onClick: menuItem.onClick,
onRenderIcon: (props: any) => <img src={menuItem.iconSrc} alt="" />
}))
}}

View File

@@ -191,7 +191,7 @@ exports[`TreeNodeComponent renders a simple node (sorted children, expanded) 1`]
"className": undefined,
"disabled": true,
"key": "menuLabel",
"onClick": [Function],
"onClick": undefined,
"onRenderIcon": [Function],
"text": "menuLabel",
},

View File

@@ -1,11 +1,12 @@
jest.mock("../../Common/DocumentClientUtilityBase");
jest.mock("../Graph/GraphExplorerComponent/GremlinClient");
jest.mock("../../Common/dataAccess/createCollection");
import * as ko from "knockout";
import * as sinon from "sinon";
import * as ViewModels from "../../Contracts/ViewModels";
import Q from "q";
import { ContainerSampleGenerator } from "./ContainerSampleGenerator";
import { createDocument } from "../../Common/DocumentClientUtilityBase";
import * as DocumentClientUtility from "../../Common/DocumentClientUtilityBase";
import { GremlinClient } from "../Graph/GraphExplorerComponent/GremlinClient";
import Explorer from "../Explorer";
import { updateUserContext } from "../../UserContext";
@@ -25,10 +26,6 @@ describe("ContainerSampleGenerator", () => {
return explorerStub;
};
beforeEach(() => {
(createDocument as jest.Mock).mockResolvedValue(undefined);
});
it("should insert documents for sql API account", async () => {
const sampleCollectionId = "SampleCollection";
const sampleDatabaseId = "SampleDB";
@@ -72,10 +69,13 @@ describe("ContainerSampleGenerator", () => {
await generator.createSampleContainerAsync();
expect(createDocument).toHaveBeenCalled();
expect(DocumentClientUtility.createDocument).toHaveBeenCalled();
});
it("should send gremlin queries for Graph API account", async () => {
sinon.stub(GremlinClient.prototype, "initialize").callsFake(() => {});
const executeStub = sinon.stub(GremlinClient.prototype, "execute").returns(Q.resolve());
updateUserContext({
databaseAccount: {
id: "foo",
@@ -121,6 +121,9 @@ describe("ContainerSampleGenerator", () => {
generator.setData(sampleData);
await generator.createSampleContainerAsync();
expect(DocumentClientUtility.createDocument).toHaveBeenCalled();
expect(executeStub.called).toBe(true);
});
it("should not create any sample for Mongo API account", async () => {
@@ -130,7 +133,7 @@ describe("ContainerSampleGenerator", () => {
explorerStub.defaultExperience = ko.observable<string>(experience);
// Rejects with error that contains experience
expect(ContainerSampleGenerator.createSampleGeneratorAsync(explorerStub)).rejects.toMatch(experience);
await expect(ContainerSampleGenerator.createSampleGeneratorAsync(explorerStub)).rejects.toMatch(experience);
});
it("should not create any sample for Table API account", async () => {

View File

@@ -1,6 +1,7 @@
import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels";
import GraphTab from ".././Tabs/GraphTab";
import { ConsoleDataType } from "../Menus/NotificationConsole/NotificationConsoleComponent";
import { GremlinClient } from "../Graph/GraphExplorerComponent/GremlinClient";
import * as NotificationConsoleUtils from "../../Utils/NotificationConsoleUtils";
import Explorer from "../Explorer";
@@ -95,9 +96,9 @@ export class ContainerSampleGenerator {
.reduce((previous, current) => previous.then(current), Promise.resolve());
} else {
// For SQL all queries are executed at the same time
this.sampleDataFile.data.map(doc => {
this.sampleDataFile.data.forEach(doc => {
const subPromise = createDocument(collection, doc);
subPromise.catch(reason => NotificationConsoleUtils.logConsoleError(reason));
subPromise.catch(reason => NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, reason));
promises.push(subPromise);
});
await Promise.all(promises);

View File

@@ -37,7 +37,7 @@ import { BindingHandlersRegisterer } from "../Bindings/BindingHandlersRegisterer
import { BrowseQueriesPane } from "./Panes/BrowseQueriesPane";
import { CassandraAPIDataClient, TableDataClient, TablesAPIDataClient } from "./Tables/TableDataClient";
import { CommandBarComponentAdapter } from "./Menus/CommandBar/CommandBarComponentAdapter";
import { configContext, Platform, updateConfigContext } from "../ConfigContext";
import { configContext, updateConfigContext } from "../ConfigContext";
import { ConsoleData, ConsoleDataType } from "./Menus/NotificationConsole/NotificationConsoleComponent";
import { decryptJWTToken, getAuthorizationHeader } from "../Utils/AuthorizationUtils";
import { DefaultExperienceUtility } from "../Shared/DefaultExperienceUtility";
@@ -58,6 +58,7 @@ import { NotebookUtil } from "./Notebook/NotebookUtil";
import { NotebookWorkspaceManager } from "../NotebookWorkspaceManager/NotebookWorkspaceManager";
import { NotificationConsoleComponentAdapter } from "./Menus/NotificationConsole/NotificationConsoleComponentAdapter";
import * as NotificationConsoleUtils from "../Utils/NotificationConsoleUtils";
import { PlatformType } from "../PlatformType";
import { QueriesClient } from "../Common/QueriesClient";
import { QuerySelectPane } from "./Panes/Tables/QuerySelectPane";
import { RenewAdHocAccessPane } from "./Panes/RenewAdHocAccessPane";
@@ -81,12 +82,12 @@ import { toRawContentUri, fromContentUri } from "../Utils/GitHubUtils";
import UserDefinedFunction from "./Tree/UserDefinedFunction";
import StoredProcedure from "./Tree/StoredProcedure";
import Trigger from "./Tree/Trigger";
import { NotificationsClientBase } from "../Common/NotificationsClientBase";
import { ContextualPaneBase } from "./Panes/ContextualPaneBase";
import TabsBase from "./Tabs/TabsBase";
import { CommandButtonComponentProps } from "./Controls/CommandButton/CommandButtonComponent";
import { updateUserContext, userContext } from "../UserContext";
import { stringToBlob } from "../Utils/BlobUtils";
import { IChoiceGroupProps } from "office-ui-fabric-react";
BindingHandlersRegisterer.registerBindingHandlers();
// Hold a reference to ComponentRegisterer to prevent transpiler to ignore import
@@ -97,6 +98,10 @@ enum ShareAccessToggleState {
Read
}
interface ExplorerOptions {
notificationsClient: NotificationsClientBase;
isEmulator: boolean;
}
interface AdHocAccessData {
readWriteUrl: string;
readUrl: string;
@@ -130,12 +135,15 @@ export default class Explorer {
public isFixedCollectionWithSharedThroughputSupported: ko.Computed<boolean>;
public isEnableMongoCapabilityPresent: ko.Computed<boolean>;
public isServerlessEnabled: ko.Computed<boolean>;
public isEmulator: boolean;
public isAccountReady: ko.Observable<boolean>;
public canSaveQueries: ko.Computed<boolean>;
public features: ko.Observable<any>;
public serverId: ko.Observable<string>;
public extensionEndpoint: ko.Observable<string>;
public armEndpoint: ko.Observable<string>;
public isTryCosmosDBSubscription: ko.Observable<boolean>;
public notificationsClient: NotificationsClientBase;
public queriesClient: QueriesClient;
public tableDataClient: TableDataClient;
public splitter: Splitter;
@@ -205,7 +213,7 @@ export default class Explorer {
public isGalleryPublishEnabled: ko.Computed<boolean>;
public isCodeOfConductEnabled: ko.Computed<boolean>;
public isLinkInjectionEnabled: ko.Computed<boolean>;
public isSettingsV2Enabled: ko.Observable<boolean>;
public isSettingsV2Enabled: ko.Computed<boolean>;
public isGitHubPaneEnabled: ko.Observable<boolean>;
public isPublishNotebookPaneEnabled: ko.Observable<boolean>;
public isCopyNotebookPaneEnabled: ko.Observable<boolean>;
@@ -264,7 +272,7 @@ export default class Explorer {
private static readonly MaxNbDatabasesToAutoExpand = 5;
constructor() {
constructor(options: ExplorerOptions) {
const startKey: number = TelemetryProcessor.traceStart(Action.InitializeDataExplorer, {
dataExplorerArea: Constants.Areas.ResourceTree
});
@@ -370,9 +378,12 @@ export default class Explorer {
}
});
this.memoryUsageInfo = ko.observable<DataModels.MemoryUsageInfo>();
this.notificationsClient = options.notificationsClient;
this.isEmulator = options.isEmulator;
this.features = ko.observable();
this.serverId = ko.observable<string>();
this.extensionEndpoint = ko.observable<string>(undefined);
this.armEndpoint = ko.observable<string>(undefined);
this.queriesClient = new QueriesClient(this);
this.isTryCosmosDBSubscription = ko.observable<boolean>(false);
@@ -412,8 +423,7 @@ export default class Explorer {
this.isLinkInjectionEnabled = ko.computed<boolean>(() =>
this.isFeatureEnabled(Constants.Features.enableLinkInjection)
);
//this.isSettingsV2Enabled = ko.computed<boolean>(() => this.isFeatureEnabled(Constants.Features.enableSettingsV2));
this.isSettingsV2Enabled = ko.observable(false);
this.isSettingsV2Enabled = ko.computed<boolean>(() => this.isFeatureEnabled(Constants.Features.enableSettingsV2));
this.isGitHubPaneEnabled = ko.observable<boolean>(false);
this.isPublishNotebookPaneEnabled = ko.observable<boolean>(false);
this.isCopyNotebookPaneEnabled = ko.observable<boolean>(false);
@@ -564,7 +574,9 @@ export default class Explorer {
this.isHostedDataExplorerEnabled = ko.computed<boolean>(
() =>
configContext.platform === Platform.Portal && !this.isRunningOnNationalCloud() && !this.isPreferredApiGraph()
this.getPlatformType() === PlatformType.Portal &&
!this.isRunningOnNationalCloud() &&
!this.isPreferredApiGraph()
);
this.isRightPanelV2Enabled = ko.computed<boolean>(() =>
this.isFeatureEnabled(Constants.Features.enableRightPanelV2)
@@ -1790,7 +1802,7 @@ export default class Explorer {
const message: any = event.data.data;
const inputs: ViewModels.DataExplorerInputsFrame = message.inputs;
const isRunningInPortal = configContext.platform === Platform.Portal;
const isRunningInPortal = window.dataExplorerPlatform == PlatformType.Portal;
const isRunningInDevMode = process.env.NODE_ENV === "development";
if (inputs && configContext.BACKEND_ENDPOINT && isRunningInPortal && isRunningInDevMode) {
inputs.extensionEndpoint = configContext.PROXY_PATH;
@@ -1858,7 +1870,7 @@ export default class Explorer {
return null;
}
if (this.selectedNode().nodeKind === "Database") {
return _.find(this.databases(), (database: ViewModels.Database) => database.id() === this.selectedNode().id());
return _.find(this.databases(), (database: ViewModels.Database) => database.rid === this.selectedNode().rid);
}
return this.findSelectedCollection().database;
}
@@ -1900,7 +1912,9 @@ export default class Explorer {
}
this.features(inputs.features);
this.serverId(inputs.serverId);
this.extensionEndpoint(inputs.extensionEndpoint || "");
this.armEndpoint(EnvironmentUtility.normalizeArmEndpointUri(inputs.csmEndpoint || configContext.ARM_ENDPOINT));
this.notificationsClient.setExtensionEndpoint(this.extensionEndpoint());
this.databaseAccount(databaseAccount);
this.subscriptionType(inputs.subscriptionType);
this.quotaId(inputs.quotaId);
@@ -1908,7 +1922,6 @@ export default class Explorer {
this.flight(inputs.addCollectionDefaultFlight);
this.isTryCosmosDBSubscription(inputs.isTryCosmosDBSubscription);
this.isAuthWithResourceToken(inputs.isAuthWithresourceToken);
this.setFeatureFlagsFromFlights(inputs.flights);
if (!!inputs.dataExplorerVersion) {
this.parentFrameDataExplorerVersion(inputs.dataExplorerVersion);
@@ -1917,7 +1930,6 @@ export default class Explorer {
this._importExplorerConfigComplete = true;
updateConfigContext({
BACKEND_ENDPOINT: inputs.extensionEndpoint || "",
ARM_ENDPOINT: this.armEndpoint()
});
@@ -1943,20 +1955,12 @@ export default class Explorer {
return Q();
}
public setFeatureFlagsFromFlights(flights: readonly string[]): void {
if (!flights) {
return;
}
if (flights.indexOf(Constants.Flights.SettingsV2) !== -1) {
this.isSettingsV2Enabled(true);
}
}
public findSelectedCollection(): ViewModels.Collection {
return (this.selectedNode().nodeKind === "Collection"
? this.selectedNode()
: this.selectedNode().collection) as ViewModels.Collection;
if (this.selectedNode().nodeKind === "Collection") {
return this.findSelectedCollectionForSelectedNode();
} else {
return this.findSelectedCollectionForSubNode();
}
}
// TODO: Refactor below methods, minimize dependencies and add unit tests where necessary
@@ -2006,6 +2010,10 @@ export default class Explorer {
this._panes.forEach((pane: ContextualPaneBase) => pane.close());
}
public getPlatformType(): PlatformType {
return window.dataExplorerPlatform;
}
public isRunningOnNationalCloud(): boolean {
return (
this.serverId() === Constants.ServerIds.blackforest ||
@@ -2069,11 +2077,11 @@ export default class Explorer {
});
databasesToLoad.forEach(async (database: ViewModels.Database) => {
await database.loadCollections();
const isNewDatabase: boolean = _.some(newDatabases, (db: ViewModels.Database) => db.id() === database.id());
const isNewDatabase: boolean = _.some(newDatabases, (db: ViewModels.Database) => db.rid === database.rid);
if (isNewDatabase) {
database.expandDatabase();
}
this.tabsManager.refreshActiveTab(tab => tab.collection && tab.collection.getDatabase().id() === database.id());
this.tabsManager.refreshActiveTab(tab => tab.collection && tab.collection.getDatabase().rid === database.rid);
});
Q.all(loadCollectionPromises).done(
@@ -2185,11 +2193,21 @@ export default class Explorer {
}
}
public findCollection(databaseId: string, collectionId: string): ViewModels.Collection {
const database: ViewModels.Database = this.databases().find(
(database: ViewModels.Database) => database.id() === databaseId
);
return database?.collections().find((collection: ViewModels.Collection) => collection.id() === collectionId);
private findSelectedCollectionForSelectedNode(): ViewModels.Collection {
return this.findCollection(this.selectedNode().rid);
}
public findCollection(rid: string): ViewModels.Collection {
for (let i = 0; i < this.databases().length; i++) {
const database = this.databases()[i];
for (let j = 0; j < database.collections().length; j++) {
const collection = database.collections()[j];
if (collection.rid === rid) {
return collection;
}
}
}
return null;
}
public isLastCollection(): boolean {
@@ -2213,7 +2231,7 @@ export default class Explorer {
const newDatabases: DataModels.Database[] = _.filter(updatedDatabaseList, (database: DataModels.Database) => {
const databaseExists = _.some(
this.databases(),
(existingDatabase: ViewModels.Database) => existingDatabase.id() === database.id
(existingDatabase: ViewModels.Database) => existingDatabase.rid === database._rid
);
return !databaseExists;
});
@@ -2225,7 +2243,7 @@ export default class Explorer {
ko.utils.arrayForEach(this.databases(), (database: ViewModels.Database) => {
const databasePresentInUpdatedList = _.some(
updatedDatabaseList,
(db: DataModels.Database) => db.id === database.id()
(db: DataModels.Database) => db._rid === database.rid
);
if (!databasePresentInUpdatedList) {
databasesToDelete.push(database);
@@ -2247,7 +2265,7 @@ export default class Explorer {
const databasesToKeep: ViewModels.Database[] = [];
ko.utils.arrayForEach(this.databases(), (database: ViewModels.Database) => {
const shouldRemoveDatabase = _.some(databasesToRemove, (db: ViewModels.Database) => db.id === database.id);
const shouldRemoveDatabase = _.some(databasesToRemove, (db: ViewModels.Database) => db.rid === database.rid);
if (!shouldRemoveDatabase) {
databasesToKeep.push(database);
}
@@ -2256,6 +2274,19 @@ export default class Explorer {
this.databases(databasesToKeep);
}
private findSelectedCollectionForSubNode(): ViewModels.Collection {
for (let i = 0; i < this.databases().length; i++) {
const database = this.databases()[i];
for (let j = 0; j < database.collections().length; j++) {
const collection = database.collections()[j];
if (this.selectedNode().collection && collection.rid === this.selectedNode().collection.rid) {
return collection;
}
}
}
return null;
}
public uploadFile(name: string, content: string, parent: NotebookContentItem): Promise<NotebookContentItem> {
if (!this.isNotebookEnabled() || !this.notebookManager?.notebookContentClient) {
const error = "Attempt to upload notebook, but notebook is not enabled";
@@ -2352,10 +2383,7 @@ export default class Explorer {
okLabel: string,
onOk: () => void,
cancelLabel: string,
onCancel: () => void,
choiceGroupProps?: IChoiceGroupProps,
textFieldProps?: TextFieldProps,
isPrimaryButtonDisabled?: boolean
onCancel: () => void
): void {
this._dialogProps({
isModal: true,
@@ -2371,10 +2399,38 @@ export default class Explorer {
onSecondaryButtonClick: () => {
this._closeModalDialog();
onCancel && onCancel();
}
});
}
public showOkCancelTextFieldModalDialog(
title: string,
msg: string,
okLabel: string,
onOk: () => void,
cancelLabel: string,
onCancel: () => void,
textFieldProps: TextFieldProps,
isPrimaryButtonDisabled?: boolean
): void {
let textFieldValue: string = null;
this._dialogProps({
isModal: true,
visible: true,
title,
subText: msg,
primaryButtonText: okLabel,
secondaryButtonText: cancelLabel,
onPrimaryButtonClick: () => {
this._closeModalDialog();
onOk && onOk();
},
choiceGroupProps,
textFieldProps,
primaryButtonDisabled: isPrimaryButtonDisabled
onSecondaryButtonClick: () => {
this._closeModalDialog();
onCancel && onCancel();
},
primaryButtonDisabled: isPrimaryButtonDisabled,
textFieldProps
});
}
@@ -2415,6 +2471,7 @@ export default class Explorer {
title: notebookContentItem.name,
tabPath: notebookContentItem.path,
collection: null,
selfLink: null,
masterKey: userContext.masterKey || "",
hashLocation: "notebooks",
isActive: ko.observable(false),
@@ -2576,7 +2633,7 @@ export default class Explorer {
const databaseAccount = this.databaseAccount();
const databaseAccountLocation = databaseAccount && databaseAccount.location.toLowerCase();
const disallowedLocationsUri = `${configContext.BACKEND_ENDPOINT}/api/disallowedLocations`;
const disallowedLocationsUri = `${this.extensionEndpoint()}/api/disallowedLocations`;
const authorizationHeader = getAuthorizationHeader();
try {
const response = await fetch(disallowedLocationsUri, {
@@ -2866,6 +2923,7 @@ export default class Explorer {
title: title,
tabPath: title,
collection: null,
selfLink: null,
hashLocation: hashLocation,
isActive: ko.observable(false),
isTabsContentExpanded: ko.observable(true),
@@ -2909,6 +2967,7 @@ export default class Explorer {
title: title,
tabPath: title,
documentClientUtility: null,
selfLink: null,
isActive: ko.observable(false),
hashLocation: hashLocation,
onUpdateTabsButtons: this.onUpdateTabsButtons,
@@ -2951,6 +3010,7 @@ export default class Explorer {
tabPath: title,
documentClientUtility: null,
collection: null,
selfLink: null,
hashLocation: hashLocation,
isActive: ko.observable(false),
isTabsContentExpanded: ko.observable(true),
@@ -3070,10 +3130,8 @@ export default class Explorer {
}
public async loadDatabaseOffers(): Promise<void> {
await Promise.all(
this.databases()?.map(async (database: ViewModels.Database) => {
await database.loadOffer();
})
);
this.databases()?.forEach(async (database: ViewModels.Database) => {
await database.loadOffer();
});
}
}

View File

@@ -32,11 +32,13 @@ export class ArraysByKeyCache<T> {
this.cache[key] = elements;
if (index < 0) {
console.error("Inserting with negative index is not allowed by ArraysByCache");
return;
}
// Check that previous index is populated, if not, ignore
if (index > elements.length) {
console.error("Inserting non-contiguous element is not allowed by ArraysByCache");
return;
}

View File

@@ -1,13 +1,13 @@
import * as ko from "knockout";
import Q from "q";
import { schemeCategory10 } from "d3-scale-chromatic";
import { selectAll, select } from "d3-selection";
import { schemeCategory20 } from "d3";
import { event as d3Event, selectAll, select } from "d3-selection";
import { zoom, zoomIdentity } from "d3-zoom";
import { scaleOrdinal } from "d3-scale";
import { forceSimulation, forceLink, forceCollide, forceManyBody } from "d3-force";
import { interpolateNumber, interpolate } from "d3-interpolate";
import { map as d3Map } from "d3-collection";
import { drag, D3DragEvent } from "d3-drag";
import { drag } from "d3-drag";
import _ from "underscore";
import { NeighborType } from "../../../Contracts/ViewModels";
@@ -89,7 +89,7 @@ export class D3ForceGraph implements GraphRenderer {
private static readonly PAGINATION_LINE2_Y_OFFSET_PX = 14;
// We limit the number of different colors to 20
private static readonly COLOR_SCHEME = scaleOrdinal(schemeCategory10);
private static readonly COLOR_SCHEME_20 = scaleOrdinal(schemeCategory20);
private static readonly MAX_COLOR_NB = 20;
// Some state variables
@@ -344,13 +344,13 @@ export class D3ForceGraph implements GraphRenderer {
} while ((el = el && el.parentNode));
}
private zoomed(event: any) {
private zoomed() {
this.zoomTransform = {
x: event.transform.x,
y: event.transform.y,
k: event.transform.k
x: d3Event.transform.x,
y: d3Event.transform.y,
k: d3Event.transform.k
};
this.g.attr("transform", event.transform);
this.g.attr("transform", d3Event.transform);
}
private instantiateSimulation() {
@@ -719,17 +719,17 @@ export class D3ForceGraph implements GraphRenderer {
})
.call(
drag()
.on("start", ((e: D3DragEvent<SVGGElement, D3Node, unknown>, d: D3Node) => {
return this.dragstarted(d, e);
}) as any)
.on("drag", ((e: D3DragEvent<SVGGElement, D3Node, unknown>, d: D3Node) => {
return this.dragged(d, e);
}) as any)
.on("end", ((e: D3DragEvent<SVGGElement, D3Node, unknown>, d: D3Node) => {
return this.dragended(d, e);
}) as any)
.on("start", (d: D3Node) => {
return this.dragstarted(d);
})
.on("drag", (d: D3Node) => {
return this.dragged(d);
})
.on("end", (d: D3Node) => {
return this.dragended(d);
})
)
.on("mouseover", (_: MouseEvent, d: D3Node) => {
.on("mouseover", (d: D3Node) => {
if (this.isHighlightDisabled || this.selectedNode || this.isDragging) {
return;
}
@@ -737,7 +737,7 @@ export class D3ForceGraph implements GraphRenderer {
this.highlightNode(this, d);
this.simulation.stop();
})
.on("mouseout", (_: MouseEvent, d: D3Node) => {
.on("mouseout", (d: D3Node) => {
if (this.isHighlightDisabled || this.selectedNode || this.isDragging) {
return;
}
@@ -765,17 +765,17 @@ export class D3ForceGraph implements GraphRenderer {
.attr("aria-label", (d: D3Node) => {
return this.retrieveNodeCaption(d);
})
.on("dblclick", function(_: MouseEvent, d: D3Node) {
.on("dblclick", function(d: D3Node) {
// this is the <g> element
self.onNodeClicked(this.parentNode, d);
})
.on("click", function(_: MouseEvent, d: D3Node) {
.on("click", function(d: D3Node) {
// this is the <g> element
self.onNodeClicked(this.parentNode, d);
})
.on("keypress", function(event: KeyboardEvent, d: D3Node) {
if (event.charCode === Constants.KeyCodes.Space || event.charCode === Constants.KeyCodes.Enter) {
event.stopPropagation();
.on("keypress", function(d: D3Node) {
if (d3Event.charCode === Constants.KeyCodes.Space || d3Event.charCode === Constants.KeyCodes.Enter) {
d3Event.stopPropagation();
// this is the <g> element
self.onNodeClicked(this.parentNode, d);
}
@@ -850,24 +850,24 @@ export class D3ForceGraph implements GraphRenderer {
return `Next page of nodes for ${this.retrieveNodeCaption(d)}`;
})
.attr("tabindex", 0)
.on("click", ((_: MouseEvent, d: D3Node) => {
.on("click", function(d: D3Node) {
self.loadNeighbors(d, PAGE_ACTION.NEXT_PAGE);
}) as any)
.on("dblclick", ((_: MouseEvent, d: D3Node) => {
})
.on("dblclick", function(d: D3Node) {
self.loadNeighbors(d, PAGE_ACTION.NEXT_PAGE);
}) as any)
.on("keypress", ((event: KeyboardEvent, d: D3Node) => {
if (event.charCode === Constants.KeyCodes.Space || event.charCode === Constants.KeyCodes.Enter) {
event.stopPropagation();
})
.on("keypress", function(d: D3Node) {
if (d3Event.charCode === Constants.KeyCodes.Space || d3Event.charCode === Constants.KeyCodes.Enter) {
d3Event.stopPropagation();
self.loadNeighbors(d, PAGE_ACTION.NEXT_PAGE);
}
}) as any)
.on("mouseover", ((e: MouseEvent, d: D3Node) => {
select(e.target as any).classed("active", true);
}) as any)
.on("mouseout", ((e: MouseEvent, d: D3Node) => {
select(e.target as any).classed("active", false);
}) as any)
})
.on("mouseover", function(d: D3Node) {
select(this).classed("active", true);
})
.on("mouseout", function(d: D3Node) {
select(this).classed("active", false);
})
.attr("visibility", (d: D3Node) => (!d._outEAllLoaded || !d._inEAllLoaded ? "visible" : "hidden"));
parent
.append("use")
@@ -879,24 +879,24 @@ export class D3ForceGraph implements GraphRenderer {
return `Previous page of nodes for ${this.retrieveNodeCaption(d)}`;
})
.attr("tabindex", 0)
.on("click", ((_: MouseEvent, d: D3Node) => {
.on("click", function(d: D3Node) {
self.loadNeighbors(d, PAGE_ACTION.PREVIOUS_PAGE);
}) as any)
.on("dblclick", ((_: MouseEvent, d: D3Node) => {
})
.on("dblclick", function(d: D3Node) {
self.loadNeighbors(d, PAGE_ACTION.PREVIOUS_PAGE);
}) as any)
.on("keypress", ((event: KeyboardEvent, d: D3Node) => {
if (event.charCode === Constants.KeyCodes.Space || event.charCode === Constants.KeyCodes.Enter) {
event.stopPropagation();
})
.on("keypress", function(d: D3Node) {
if (d3Event.charCode === Constants.KeyCodes.Space || d3Event.charCode === Constants.KeyCodes.Enter) {
d3Event.stopPropagation();
self.loadNeighbors(d, PAGE_ACTION.PREVIOUS_PAGE);
}
}) as any)
.on("mouseover", ((e: MouseEvent, d: D3Node) => {
select(e.target as any).classed("active", true);
}) as any)
.on("mouseout", ((e: MouseEvent, d: D3Node) => {
select(e.target as any).classed("active", false);
}) as any)
})
.on("mouseover", function(d: D3Node) {
select(this).classed("active", true);
})
.on("mouseout", function(d: D3Node) {
select(this).classed("active", false);
})
.attr("visibility", (d: D3Node) =>
!d._pagination || d._pagination.currentPage.start !== 0 ? "visible" : "hidden"
);
@@ -975,24 +975,24 @@ export class D3ForceGraph implements GraphRenderer {
return `Load adjacent nodes for ${this.retrieveNodeCaption(d)}`;
})
.attr("tabindex", 0)
.on("click", ((_: MouseEvent, d: D3Node) => {
.on("click", function(d: D3Node) {
self.loadNeighbors(d, PAGE_ACTION.FIRST_PAGE);
}) as any)
.on("dblclick", ((_: MouseEvent, d: D3Node) => {
})
.on("dblclick", function(d: D3Node) {
self.loadNeighbors(d, PAGE_ACTION.FIRST_PAGE);
}) as any)
.on("keypress", ((event: KeyboardEvent, d: D3Node) => {
if (event.charCode === Constants.KeyCodes.Space || event.charCode === Constants.KeyCodes.Enter) {
event.stopPropagation();
})
.on("keypress", function(d: D3Node) {
if (d3Event.charCode === Constants.KeyCodes.Space || d3Event.charCode === Constants.KeyCodes.Enter) {
d3Event.stopPropagation();
self.loadNeighbors(d, PAGE_ACTION.FIRST_PAGE);
}
}) as any)
.on("mouseover", ((e: MouseEvent, d: D3Node) => {
select(e.target as any).classed("active", true);
}) as any)
.on("mouseout", ((e: MouseEvent, d: D3Node) => {
select(e.target as any).classed("active", false);
}) as any);
})
.on("mouseover", function(d: D3Node) {
select(this).classed("active", true);
})
.on("mouseout", function(d: D3Node) {
select(this).classed("active", false);
});
}
/**
@@ -1053,7 +1053,7 @@ export class D3ForceGraph implements GraphRenderer {
if (index < 0 || index >= D3ForceGraph.MAX_COLOR_NB) {
index = D3ForceGraph.MAX_COLOR_NB - 1;
}
return D3ForceGraph.COLOR_SCHEME(index.toString());
return D3ForceGraph.COLOR_SCHEME_20(index.toString());
}
/**
@@ -1071,23 +1071,23 @@ export class D3ForceGraph implements GraphRenderer {
}
}
private dragstarted(d: D3Node, event: D3DragEvent<SVGGElement, D3Node, unknown>) {
private dragstarted(d: D3Node) {
this.isDragging = true;
if (!event.active) {
if (!d3Event.active) {
this.simulation.alphaTarget(0.3).restart();
}
d.fx = d.x;
d.fy = d.y;
}
private dragged(d: D3Node, event: D3DragEvent<SVGGElement, D3Node, unknown>) {
d.fx = event.x;
d.fy = event.y;
private dragged(d: D3Node) {
d.fx = d3Event.x;
d.fy = d3Event.y;
}
private dragended(d: D3Node, event: D3DragEvent<SVGGElement, D3Node, unknown>) {
private dragended(d: D3Node) {
this.isDragging = false;
if (!event.active) {
if (!d3Event.active) {
this.simulation.alphaTarget(0);
}

View File

@@ -141,6 +141,8 @@ describe("getPkIdFromDocumentId", () => {
});
describe("GraphExplorer", () => {
const COLLECTION_RID = "collectionRid";
const COLLECTION_SELF_LINK = "collectionSelfLink";
const gremlinRU = 789.12;
const createMockProps = (): GraphExplorerProps => {
@@ -158,6 +160,8 @@ describe("GraphExplorer", () => {
onIsValidQueryChange: (isValidQuery: boolean): void => {},
collectionPartitionKeyProperty: "collectionPartitionKeyProperty",
collectionRid: COLLECTION_RID,
collectionSelfLink: COLLECTION_SELF_LINK,
graphBackendEndpoint: "graphBackendEndpoint",
databaseId: "databaseId",
collectionId: "collectionId",

View File

@@ -47,6 +47,8 @@ export interface GraphExplorerProps {
onIsValidQueryChange: (isValidQuery: boolean) => void;
collectionPartitionKeyProperty: string;
collectionRid: string;
collectionSelfLink: string;
graphBackendEndpoint: string;
databaseId: string;
collectionId: string;
@@ -1759,7 +1761,7 @@ export class GraphExplorer extends React.Component<GraphExplorerProps, GraphExpl
const id = GraphExplorer.reportToConsole(ConsoleDataType.InProgress, `Executing: ${queryInfoStr}`);
return queryDocumentsPage(
this.props.collectionId,
this.props.collectionRid,
this.currentDocDBQueryInfo.iterator,
this.currentDocDBQueryInfo.index,
{

View File

@@ -17,6 +17,8 @@ interface Parameter {
graphConfig?: GraphConfig;
collectionPartitionKeyProperty: string;
collectionRid: string;
collectionSelfLink: string;
graphBackendEndpoint: string;
databaseId: string;
collectionId: string;
@@ -47,6 +49,8 @@ export class GraphExplorerAdapter implements ReactAdapter {
onIsGraphDisplayed={this.params.onIsGraphDisplayed}
onResetDefaultGraphConfigValues={this.params.onResetDefaultGraphConfigValues}
collectionPartitionKeyProperty={this.params.collectionPartitionKeyProperty}
collectionRid={this.params.collectionRid}
collectionSelfLink={this.params.collectionSelfLink}
graphBackendEndpoint={this.params.graphBackendEndpoint}
databaseId={this.params.databaseId}
collectionId={this.params.collectionId}

View File

@@ -192,6 +192,7 @@ export class GremlinClient {
}
private static reportError(msg: string): void {
console.error(msg);
NotificationConsoleUtils.logConsoleMessage(ConsoleDataType.Error, msg);
Logger.logError(msg, GremlinClient.LOG_AREA);
}

View File

@@ -136,6 +136,7 @@ export class GremlinSimpleClient {
const data = typeof msg.data === "string" ? msg.data : new TextDecoder("utf-8").decode(msg.data);
return JSON.parse(data);
} catch (e) {
console.error(e, msg);
if (this.params.failureCallback) {
this.params.failureCallback(
null,

View File

@@ -1,6 +0,0 @@
/* eslint-disable */
export class GremlinClient {
constructor() {}
initialize() {}
execute() {}
}

View File

@@ -10,7 +10,7 @@ import * as ViewModels from "../../../Contracts/ViewModels";
import { CommandBarComponentButtonFactory } from "./CommandBarComponentButtonFactory";
import { CommandBar, ICommandBarItemProps } from "office-ui-fabric-react/lib/CommandBar";
import { StyleConstants } from "../../../Common/Constants";
import * as CommandBarUtil from "./CommandBarUtil";
import { CommandBarUtil } from "./CommandBarUtil";
import Explorer from "../../Explorer";
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";

View File

@@ -1,4 +1,5 @@
import * as ViewModels from "../../../Contracts/ViewModels";
import { PlatformType } from "../../../PlatformType";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
import { Areas } from "../../../Common/Constants";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
@@ -158,7 +159,7 @@ export class CommandBarComponentButtonFactory {
public static createControlCommandBarButtons(container: Explorer): CommandButtonComponentProps[] {
const buttons: CommandButtonComponentProps[] = [];
if (configContext.platform === Platform.Hosted) {
if (window.dataExplorerPlatform === PlatformType.Hosted) {
return buttons;
}
@@ -193,7 +194,7 @@ export class CommandBarComponentButtonFactory {
buttons.push(fullScreenButton);
}
if (configContext.platform !== Platform.Emulator) {
if (!container.hasOwnProperty("isEmulator") || !container.isEmulator) {
const label = "Feedback";
const feedbackButtonOptions: CommandButtonComponentProps = {
iconSrc: FeedbackIcon,

View File

@@ -1,4 +1,4 @@
import * as CommandBarUtil from "./CommandBarUtil";
import { CommandBarUtil } from "./CommandBarUtil";
import * as ViewModels from "../../../Contracts/ViewModels";
import { ICommandBarItemProps } from "office-ui-fabric-react/lib/CommandBar";
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
@@ -8,7 +8,7 @@ describe("CommandBarUtil tests", () => {
return {
iconSrc: "icon",
iconAlt: "label",
onCommandClick: jest.fn(),
onCommandClick: (e: React.SyntheticEvent): void => {},
commandButtonLabel: "label",
ariaLabel: "ariaLabel",
hasPopup: true,
@@ -29,14 +29,11 @@ describe("CommandBarUtil tests", () => {
expect(!converted.split);
expect(converted.iconProps.imageProps.src).toEqual(btn.iconSrc);
expect(converted.iconProps.imageProps.alt).toEqual(btn.iconAlt);
expect(converted.onClick).toEqual(btn.onCommandClick);
expect(converted.text).toEqual(btn.commandButtonLabel);
expect(converted.ariaLabel).toEqual(btn.ariaLabel);
expect(converted.disabled).toEqual(btn.disabled);
expect(converted.className).toEqual(btn.className);
// Click gets called
converted.onClick();
expect(btn.onCommandClick).toBeCalled();
});
it("should convert NavbarButtonConfig to split button", () => {

View File

@@ -11,187 +11,177 @@ import ChevronDownIcon from "../../../../images/Chevron_down.svg";
import { ArcadiaMenuPicker } from "../../Controls/Arcadia/ArcadiaMenuPicker";
import { MemoryTrackerComponent } from "./MemoryTrackerComponent";
import { MemoryUsageInfo } from "../../../Contracts/DataModels";
import * as TelemetryProcessor from "../../../Shared/Telemetry/TelemetryProcessor";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
/**
* Convert our NavbarButtonConfig to UI Fabric buttons
* @param btns
* Utilities for CommandBar
*/
export const convertButton = (btns: CommandButtonComponentProps[], backgroundColor: string): ICommandBarItemProps[] => {
const buttonHeightPx = StyleConstants.CommandBarButtonHeight;
export class CommandBarUtil {
/**
* Convert our NavbarButtonConfig to UI Fabric buttons
* @param btns
*/
public static convertButton(btns: CommandButtonComponentProps[], backgroundColor: string): ICommandBarItemProps[] {
const buttonHeightPx = StyleConstants.CommandBarButtonHeight;
return btns
.filter(btn => btn)
.map(
(btn: CommandButtonComponentProps, index: number): ICommandBarItemProps => {
if (btn.isDivider) {
return createDivider(btn.commandButtonLabel);
}
return btns
.filter(btn => btn)
.map(
(btn: CommandButtonComponentProps, index: number): ICommandBarItemProps => {
if (btn.isDivider) {
return CommandBarUtil.createDivider(btn.commandButtonLabel);
}
const isSplit = !!btn.children && btn.children.length > 0;
const label = btn.commandButtonLabel || btn.tooltipText;
const result: ICommandBarItemProps = {
iconProps: {
style: {
width: StyleConstants.CommandBarIconWidth, // 16
alignSelf: btn.iconName ? "baseline" : undefined
},
imageProps: btn.iconSrc ? { src: btn.iconSrc, alt: btn.iconAlt } : undefined,
iconName: btn.iconName
},
onClick: (ev?: React.MouseEvent<HTMLElement, MouseEvent> | React.KeyboardEvent<HTMLElement>) => {
btn.onCommandClick(ev);
TelemetryProcessor.trace(Action.ClickCommandBarButton, ActionModifiers.Mark, { label });
},
key: `${btn.commandButtonLabel}${index}`,
text: label,
"data-test": label,
title: btn.tooltipText,
name: label,
disabled: btn.disabled,
ariaLabel: btn.ariaLabel,
buttonStyles: {
root: {
backgroundColor: backgroundColor,
height: buttonHeightPx,
paddingRight: 0,
paddingLeft: 0,
minWidth: 24,
marginLeft: isSplit ? 0 : 5,
marginRight: isSplit ? 0 : 5
},
rootDisabled: {
backgroundColor: backgroundColor,
pointerEvents: "auto"
},
splitButtonMenuButton: {
backgroundColor: backgroundColor,
selectors: {
":hover": { backgroundColor: StyleConstants.AccentLight }
const isSplit = !!btn.children && btn.children.length > 0;
const result: ICommandBarItemProps = {
iconProps: {
style: {
width: StyleConstants.CommandBarIconWidth, // 16
alignSelf: btn.iconName ? "baseline" : undefined
},
width: 16
imageProps: btn.iconSrc ? { src: btn.iconSrc, alt: btn.iconAlt } : undefined,
iconName: btn.iconName
},
label: { fontSize: StyleConstants.mediumFontSize },
rootHovered: { backgroundColor: StyleConstants.AccentLight },
rootPressed: { backgroundColor: StyleConstants.AccentLight },
splitButtonMenuButtonExpanded: {
backgroundColor: StyleConstants.AccentExtra,
selectors: {
":hover": { backgroundColor: StyleConstants.AccentLight }
onClick: btn.onCommandClick,
key: `${btn.commandButtonLabel}${index}`,
text: btn.commandButtonLabel || btn.tooltipText,
"data-test": btn.commandButtonLabel || btn.tooltipText,
title: btn.tooltipText,
name: btn.commandButtonLabel || btn.tooltipText,
disabled: btn.disabled,
ariaLabel: btn.ariaLabel,
buttonStyles: {
root: {
backgroundColor: backgroundColor,
height: buttonHeightPx,
paddingRight: 0,
paddingLeft: 0,
minWidth: 24,
marginLeft: isSplit ? 0 : 5,
marginRight: isSplit ? 0 : 5
},
rootDisabled: {
backgroundColor: backgroundColor,
pointerEvents: "auto"
},
splitButtonMenuButton: {
backgroundColor: backgroundColor,
selectors: {
":hover": { backgroundColor: StyleConstants.AccentLight }
},
width: 16
},
label: { fontSize: StyleConstants.mediumFontSize },
rootHovered: { backgroundColor: StyleConstants.AccentLight },
rootPressed: { backgroundColor: StyleConstants.AccentLight },
splitButtonMenuButtonExpanded: {
backgroundColor: StyleConstants.AccentExtra,
selectors: {
":hover": { backgroundColor: StyleConstants.AccentLight }
}
},
splitButtonDivider: {
display: "none"
},
icon: {
paddingLeft: 0,
paddingRight: 0
},
splitButtonContainer: {
marginLeft: 5,
marginRight: 5
}
},
splitButtonDivider: {
display: "none"
},
icon: {
paddingLeft: 0,
paddingRight: 0
},
splitButtonContainer: {
marginLeft: 5,
marginRight: 5
}
},
className: btn.className,
id: btn.id
};
className: btn.className,
id: btn.id
};
if (isSplit) {
// It's a split button
result.split = true;
if (isSplit) {
// It's a split button
result.split = true;
result.subMenuProps = {
items: convertButton(btn.children, backgroundColor),
styles: {
list: {
// TODO Figure out how to do it the proper way with subComponentStyles.
// TODO Remove all this crazy styling once we adopt Ui-Fabric Azure themes
selectors: {
".ms-ContextualMenu-itemText": { fontSize: StyleConstants.mediumFontSize },
".ms-ContextualMenu-link:hover": { backgroundColor: StyleConstants.AccentLight },
".ms-ContextualMenu-icon": { width: 16, height: 16 }
result.subMenuProps = {
items: CommandBarUtil.convertButton(btn.children, backgroundColor),
styles: {
list: {
// TODO Figure out how to do it the proper way with subComponentStyles.
// TODO Remove all this crazy styling once we adopt Ui-Fabric Azure themes
selectors: {
".ms-ContextualMenu-itemText": { fontSize: StyleConstants.mediumFontSize },
".ms-ContextualMenu-link:hover": { backgroundColor: StyleConstants.AccentLight },
".ms-ContextualMenu-icon": { width: 16, height: 16 }
}
}
}
}
};
};
result.menuIconProps = {
iconType: IconType.image,
style: {
width: 12,
paddingLeft: 1,
paddingTop: 6
},
imageProps: { src: ChevronDownIcon, alt: btn.iconAlt }
};
result.menuIconProps = {
iconType: IconType.image,
style: {
width: 12,
paddingLeft: 1,
paddingTop: 6
},
imageProps: { src: ChevronDownIcon, alt: btn.iconAlt }
};
}
if (btn.isDropdown) {
const selectedChild = _.find(btn.children, child => child.dropdownItemKey === btn.dropdownSelectedKey);
result.name = selectedChild?.commandButtonLabel || btn.dropdownPlaceholder;
const dropdownStyles: Partial<IDropdownStyles> = {
root: { margin: 5 },
dropdown: { width: btn.dropdownWidth },
title: { fontSize: 12, height: 30, lineHeight: 28 },
dropdownItem: { fontSize: 12, lineHeight: 28, minHeight: 30 },
dropdownItemSelected: { fontSize: 12, lineHeight: 28, minHeight: 30 }
};
result.commandBarButtonAs = (props: IComponentAsProps<ICommandBarItemProps>) => {
return (
<Dropdown
placeholder={btn.dropdownPlaceholder}
defaultSelectedKey={btn.dropdownSelectedKey}
onChange={(event: React.FormEvent<HTMLDivElement>, option?: IDropdownOption, index?: number): void =>
btn.children[index].onCommandClick(event)
}
options={btn.children.map((child: CommandButtonComponentProps) => ({
key: child.dropdownItemKey,
text: child.commandButtonLabel
}))}
styles={dropdownStyles}
/>
);
};
}
if (btn.isArcadiaPicker && btn.arcadiaProps) {
result.commandBarButtonAs = () => <ArcadiaMenuPicker {...btn.arcadiaProps} />;
}
return result;
}
);
}
if (btn.isDropdown) {
const selectedChild = _.find(btn.children, child => child.dropdownItemKey === btn.dropdownSelectedKey);
result.name = selectedChild?.commandButtonLabel || btn.dropdownPlaceholder;
public static createDivider(key: string): ICommandBarItemProps {
return {
onRender: () => (
<div className="dividerContainer">
<span />
</div>
),
iconOnly: true,
disabled: true,
key: key
};
}
const dropdownStyles: Partial<IDropdownStyles> = {
root: { margin: 5 },
dropdown: { width: btn.dropdownWidth },
title: { fontSize: 12, height: 30, lineHeight: 28 },
dropdownItem: { fontSize: 12, lineHeight: 28, minHeight: 30 },
dropdownItemSelected: { fontSize: 12, lineHeight: 28, minHeight: 30 }
};
const onDropdownChange = (
event: React.FormEvent<HTMLDivElement>,
option?: IDropdownOption,
index?: number
): void => {
btn.children[index].onCommandClick(event);
TelemetryProcessor.trace(Action.ClickCommandBarButton, ActionModifiers.Mark, { label: option.text });
};
result.commandBarButtonAs = (props: IComponentAsProps<ICommandBarItemProps>) => {
return (
<Dropdown
placeholder={btn.dropdownPlaceholder}
defaultSelectedKey={btn.dropdownSelectedKey}
onChange={onDropdownChange}
options={btn.children.map((child: CommandButtonComponentProps) => ({
key: child.dropdownItemKey,
text: child.commandButtonLabel
}))}
styles={dropdownStyles}
/>
);
};
}
if (btn.isArcadiaPicker && btn.arcadiaProps) {
result.commandBarButtonAs = () => <ArcadiaMenuPicker {...btn.arcadiaProps} />;
}
return result;
}
);
};
export const createDivider = (key: string): ICommandBarItemProps => {
return {
onRender: () => (
<div className="dividerContainer">
<span />
</div>
),
iconOnly: true,
disabled: true,
key: key
};
};
export const createMemoryTracker = (
key: string,
memoryUsageInfo: Observable<MemoryUsageInfo>
): ICommandBarItemProps => {
return {
key,
onRender: () => <MemoryTrackerComponent memoryUsageInfo={memoryUsageInfo} />
};
};
public static createMemoryTracker(key: string, memoryUsageInfo: Observable<MemoryUsageInfo>): ICommandBarItemProps {
return {
key,
onRender: () => <MemoryTrackerComponent memoryUsageInfo={memoryUsageInfo} />
};
}
}

View File

@@ -134,13 +134,10 @@ export class NotificationConsoleComponent extends React.Component<
className="expandCollapseButton"
role="button"
tabIndex={0}
aria-label={"console button" + (this.state.isExpanded ? " collapsed" : " expanded")}
aria-expanded={!this.state.isExpanded}
aria-label={this.state.isExpanded ? "collapse console" : "expand console"}
aria-expanded={this.state.isExpanded}
>
<img
src={this.state.isExpanded ? ChevronDownIcon : ChevronUpIcon}
alt={this.state.isExpanded ? "ChevronDownIcon" : "ChevronUpIcon"}
/>
<img src={this.state.isExpanded ? ChevronDownIcon : ChevronUpIcon} alt="" />
</div>
</div>
<AnimateHeight

View File

@@ -68,14 +68,14 @@ exports[`NotificationConsoleComponent renders the console (expanded) 1`] = `
</span>
</div>
<div
aria-expanded={false}
aria-label="console button collapsed"
aria-expanded={true}
aria-label="collapse console"
className="expandCollapseButton"
role="button"
tabIndex={0}
>
<img
alt="ChevronDownIcon"
alt=""
src=""
/>
</div>

View File

@@ -15,10 +15,7 @@ export interface OpenNotebookItem {
path: string;
}
export interface OpenCollectionItem {
databaseId: string;
collectionId: string;
}
export type OpenCollectionItem = string;
export interface Item {
type: Type;
@@ -124,11 +121,7 @@ export class MostRecentActivity {
public onItemClicked(item: Item) {
switch (item.type) {
case Type.OpenCollection: {
const openCollectionitem = item.data as OpenCollectionItem;
const collection = this.container.findCollection(
openCollectionitem.databaseId,
openCollectionitem.collectionId
);
const collection = this.container.findCollection(item.data as OpenCollectionItem);
if (collection) {
collection.openTab();
}

Some files were not shown because too many files have changed in this diff Show More