initial commit
This commit is contained in:
Родитель
c7aeea80f0
Коммит
cb3fa8b916
|
@ -0,0 +1,4 @@
|
|||
node_modules
|
||||
scripts/*.js
|
||||
typings
|
||||
dist
|
|
@ -0,0 +1,18 @@
|
|||
// A task runner configuration.
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"command": "grunt",
|
||||
"isShellCommand": true,
|
||||
"tasks": [
|
||||
{
|
||||
"taskName": "build",
|
||||
"isBuildCommand": true,
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"taskName": "publish",
|
||||
"isBuildCommand": false,
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"id": "hitcount-control-dev",
|
||||
"name": "Hitcount Control (dev)",
|
||||
"public": false
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"public": true
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
# vsts-extension-ts-seed-simple #
|
||||
|
||||
Describe your extension here. This description will be shown in the marketplace. You can use *Markdown*.
|
|
@ -0,0 +1,84 @@
|
|||
module.exports = function (grunt) {
|
||||
grunt.initConfig({
|
||||
ts: {
|
||||
build: {
|
||||
tsconfig: true,
|
||||
"outDir": "./dist/scripts"
|
||||
},
|
||||
buildTest: {
|
||||
tsconfig: true,
|
||||
"outDir": "./test/scripts",
|
||||
src: ["./scripts/**/*.tests.ts"]
|
||||
},
|
||||
options: {
|
||||
fast: 'never'
|
||||
}
|
||||
},
|
||||
exec: {
|
||||
package_dev: {
|
||||
command: "tfx extension create --root dist --manifest-globs vss-extension.json --overrides-file configs/dev.json",
|
||||
stdout: true,
|
||||
stderr: true
|
||||
},
|
||||
package_release: {
|
||||
command: "tfx extension create --root dist --manifest-globs vss-extension.json --overrides-file configs/release.json",
|
||||
stdout: true,
|
||||
stderr: true
|
||||
},
|
||||
publish_dev: {
|
||||
command: "tfx extension publish --service-url https://marketplace.visualstudio.com --root dist --manifest-globs vss-extension.json --overrides-file configs/dev.json",
|
||||
stdout: true,
|
||||
stderr: true
|
||||
},
|
||||
publish_release: {
|
||||
command: "tfx extension publish --service-url https://marketplace.visualstudio.com --root dist --manifest-globs vss-extension.json --overrides-file configs/release.json",
|
||||
stdout: true,
|
||||
stderr: true
|
||||
}
|
||||
},
|
||||
copy: {
|
||||
scripts: {
|
||||
files: [{
|
||||
expand: true,
|
||||
flatten: true,
|
||||
src: ["node_modules/vss-web-extension-sdk/lib/VSS.SDK.min.js"],
|
||||
dest: "dist/scripts",
|
||||
filter: "isFile"
|
||||
},
|
||||
{
|
||||
expand: true,
|
||||
flatten: false,
|
||||
src: ["styles/**", "img/**", "*.html", "vss-extension.json", "*.md"],
|
||||
dest: "dist"
|
||||
}]
|
||||
}
|
||||
},
|
||||
|
||||
clean: ["scripts/**/*.js", "*.vsix", "dist", "test"],
|
||||
|
||||
karma: {
|
||||
unit: {
|
||||
configFile: 'karma.conf.js',
|
||||
singleRun: true,
|
||||
browsers: ["PhantomJS"]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks("grunt-ts");
|
||||
grunt.loadNpmTasks("grunt-exec");
|
||||
grunt.loadNpmTasks("grunt-contrib-copy");
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-karma');
|
||||
|
||||
grunt.registerTask("build", ["ts:build", "copy:scripts"]);
|
||||
|
||||
grunt.registerTask("test", ["ts:buildTest", "karma:unit"]);
|
||||
|
||||
grunt.registerTask("package-dev", ["build", "exec:package_dev"]);
|
||||
grunt.registerTask("package-release", ["build", "exec:package_release"]);
|
||||
grunt.registerTask("publish-dev", ["package-dev", "exec:publish_dev"]);
|
||||
grunt.registerTask("publish-release", ["package-release", "exec:publish_release"]);
|
||||
|
||||
grunt.registerTask("default", ["package-dev"]);
|
||||
};
|
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en" style="height:100%">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<!-- VSS Framework -->
|
||||
<script src="scripts/VSS.SDK.min.js"></script>
|
||||
|
||||
<title>Color Control</title>
|
||||
<link rel="stylesheet" href="styles/style.css" type="text/css"/>
|
||||
</head>
|
||||
|
||||
<body style="height:100%">
|
||||
<script>
|
||||
VSS.init({
|
||||
explicitNotifyLoaded: true,
|
||||
usePlatformScripts: true
|
||||
});
|
||||
|
||||
// Load main entry point for extension
|
||||
VSS.require(["scripts/app"], function () {
|
||||
// loading succeeded
|
||||
VSS.notifyLoadSucceeded();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,58 @@
|
|||
// Karma configuration
|
||||
// Generated on Thu Jun 30 2016 14:46:40 GMT-0700 (Pacific Daylight Time)
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
// base path that will be used to resolve all patterns (eg. files, exclude)
|
||||
basePath: '',
|
||||
|
||||
// frameworks to use
|
||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
frameworks: ['mocha', 'requirejs', 'chai'],
|
||||
|
||||
// list of files / patterns to load in the browser
|
||||
files: [
|
||||
{ pattern: 'test/**/*.js', included: false },
|
||||
'test-main.js'
|
||||
],
|
||||
|
||||
// list of files to exclude
|
||||
exclude: [
|
||||
],
|
||||
|
||||
// preprocess matching files before serving them to the browser
|
||||
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
||||
preprocessors: {
|
||||
},
|
||||
|
||||
// test results reporter to use
|
||||
// possible values: 'dots', 'progress'
|
||||
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
||||
reporters: ['progress'],
|
||||
|
||||
// web server port
|
||||
port: 9876,
|
||||
|
||||
// enable / disable colors in the output (reporters and logs)
|
||||
colors: true,
|
||||
|
||||
// level of logging
|
||||
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
|
||||
logLevel: config.LOG_INFO,
|
||||
|
||||
// enable / disable watching file and executing tests whenever any file changes
|
||||
autoWatch: false,
|
||||
|
||||
// start these browsers
|
||||
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
|
||||
browsers: ['PhantomJS'],
|
||||
|
||||
// Continuous Integration mode
|
||||
// if true, Karma captures browsers, runs the tests and exits
|
||||
singleRun: true,
|
||||
|
||||
// Concurrency level
|
||||
// how many browser should be started simultaneous
|
||||
concurrency: Infinity
|
||||
})
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"devDependencies": {
|
||||
"chai": "^3.5.0",
|
||||
"grunt": "~1.0.1",
|
||||
"grunt-cli": "^1.2.0",
|
||||
"grunt-contrib-clean": "^1.0.0",
|
||||
"grunt-contrib-copy": "~1.0.0",
|
||||
"grunt-exec": "~0.4.7",
|
||||
"grunt-karma": "^2.0.0",
|
||||
"grunt-ts": "^5.5.1",
|
||||
"karma": "^1.1.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-mocha": "^1.1.1",
|
||||
"karma-phantomjs-launcher": "^1.0.1",
|
||||
"karma-requirejs": "^1.0.0",
|
||||
"mocha": "^2.5.3",
|
||||
"requirejs": "^2.2.0",
|
||||
"tfx-cli": "^0.3.13",
|
||||
"typescript": "^1.8.10",
|
||||
"typings": "^1.0.4",
|
||||
"vss-web-extension-sdk": "^1.100.0"
|
||||
},
|
||||
"name": "vsts-extension-ts-seed-simple",
|
||||
"private": true,
|
||||
"version": "0.5.0",
|
||||
"scripts": {
|
||||
"postinstall": "typings install"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/// <reference path="../typings/index.d.ts" />
|
||||
import { Controller } from "./control";
|
||||
import * as ExtensionContracts from "TFS/WorkItemTracking/ExtensionContracts";
|
||||
|
||||
var control: Controller;
|
||||
|
||||
var provider = () => {
|
||||
return {
|
||||
onLoaded: (workItemLoadedArgs: ExtensionContracts.IWorkItemLoadedArgs) => {
|
||||
control = new Controller();
|
||||
},
|
||||
onFieldChanged: (fieldChangedArgs: ExtensionContracts.IWorkItemFieldChangedArgs) => {
|
||||
var changedValue = fieldChangedArgs.changedFields[control.getFieldName()];
|
||||
if (changedValue !== undefined) {
|
||||
control.updateExternal(changedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
VSS.register("mariamclaughlin.hitcount-control-dev.hitcount-control-contribution", provider);
|
|
@ -0,0 +1,83 @@
|
|||
|
||||
/** The class control.ts will orchestrate the classes of InputParser, Model and View
|
||||
* in order to perform the required actions of the extensions.
|
||||
*/
|
||||
import * as VSSService from "VSS/Service";
|
||||
import * as WitService from "TFS/WorkItemTracking/Services";
|
||||
import * as ExtensionContracts from "TFS/WorkItemTracking/ExtensionContracts";
|
||||
import { Model } from "./model";
|
||||
import { View } from "./view";
|
||||
import { ErrorView } from "./errorView";
|
||||
import * as Q from "q";
|
||||
|
||||
export class Controller {
|
||||
|
||||
private _fieldName: string = "";
|
||||
|
||||
private _inputs: IDictionaryStringTo<string>;
|
||||
|
||||
private _model: Model;
|
||||
|
||||
private _view: View;
|
||||
|
||||
constructor() {
|
||||
this._initialize();
|
||||
}
|
||||
|
||||
private _initialize(): void {
|
||||
|
||||
this._inputs = VSS.getConfiguration().witInputs;
|
||||
this._fieldName = this._inputs["FieldName"];
|
||||
|
||||
WitService.WorkItemFormService.getService().then(
|
||||
(service) => {
|
||||
Q.spread(
|
||||
[service.getFieldValue(this._fieldName)],
|
||||
(currentValue: number) => {
|
||||
// Dependent on view, model, and inputParser refactoring
|
||||
this._model = new Model(Number(currentValue));
|
||||
this._view = new View(this._model, (val) => {
|
||||
this._updateInternal(val);
|
||||
}, () => {
|
||||
this._model.incrementValue();
|
||||
this._updateInternal(this._model.getCurrentValue());
|
||||
}, () => {
|
||||
this._model.decrementValue();
|
||||
this._updateInternal(this._model.getCurrentValue());
|
||||
});
|
||||
}, this._handleError
|
||||
).then(null, this._handleError);
|
||||
},
|
||||
this._handleError);
|
||||
}
|
||||
|
||||
private _handleError(error: string): void {
|
||||
let errorView = new ErrorView(error);
|
||||
}
|
||||
|
||||
private _updateInternal(value: number): void {
|
||||
WitService.WorkItemFormService.getService().then(
|
||||
(service) => {
|
||||
service.setFieldValue(this._fieldName, value).then(
|
||||
() => {
|
||||
this._update(value);
|
||||
}, this._handleError)
|
||||
},
|
||||
this._handleError
|
||||
);
|
||||
}
|
||||
|
||||
private _update(value: number): void {
|
||||
this._model.setCurrentValue(Number(value));
|
||||
this._view.update(value);
|
||||
}
|
||||
|
||||
public updateExternal(value: number): void {
|
||||
this._update(Number(value));
|
||||
}
|
||||
|
||||
public getFieldName(): string {
|
||||
return this._fieldName;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/***************************************************************************
|
||||
Purpose: This class is being used to get errors from an input parser and
|
||||
a model. It takes all the errors and put them in an array in
|
||||
order to be sent to a view to display them.
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
// shows the errors in the control container rather than the control.
|
||||
export class ErrorView {
|
||||
|
||||
constructor(error: string) {
|
||||
// container div
|
||||
var container = $("<div />");
|
||||
container.addClass("container");
|
||||
|
||||
// create an icon and text for the error
|
||||
var warning = $("<p />");
|
||||
warning.text(error);
|
||||
warning.attr("title", error);
|
||||
container.append(warning);
|
||||
|
||||
|
||||
// include documentation link for help.
|
||||
var help = $("<p />");
|
||||
help.text("See ");
|
||||
|
||||
var a = $("<a> </a>");
|
||||
a.attr("href", "https://www.visualstudio.com/en-us/products/visual-studio-team-services-vs.aspx");
|
||||
a.attr("target", "_blank");
|
||||
a.text("Documentation.");
|
||||
|
||||
help.append(a);
|
||||
container.append(help);
|
||||
|
||||
$('body').empty().append(container);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import { expect } from 'chai';
|
||||
import { Model } from './model';
|
||||
|
||||
describe("Model", () => {
|
||||
let model: Model;
|
||||
|
||||
beforeEach(() => {
|
||||
model = new Model(0);
|
||||
});
|
||||
|
||||
it("current value of 0", () => {
|
||||
expect(model.getCurrentValue()).to.be.deep.equal(0);
|
||||
});
|
||||
|
||||
it("next value from 0", () => {
|
||||
model.incrementValue();
|
||||
expect(model.getCurrentValue()).to.be.deep.equal(1);
|
||||
});
|
||||
|
||||
it("previous value of 0", () => {
|
||||
model.decrementValue();
|
||||
expect(model.getCurrentValue()).to.be.deep.equal(0);
|
||||
});
|
||||
|
||||
it("previous and previous value of 20 is 18", () => {
|
||||
model.setCurrentValue(20);
|
||||
model.decrementValue();
|
||||
model.decrementValue();
|
||||
expect(model.getCurrentValue()).to.be.deep.equal(18);
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
export class Model {
|
||||
|
||||
/**
|
||||
* Model takes the initial value from Control and sets it to the current value
|
||||
* selected in the Hit Count custom control. This will be updated in View and
|
||||
* changes as the user increments and decrements the value.
|
||||
*/
|
||||
|
||||
constructor(initialValue: number) {
|
||||
this._currentValue = initialValue;
|
||||
}
|
||||
|
||||
private _currentValue: number;
|
||||
|
||||
public setCurrentValue(value: number) {
|
||||
if (value === undefined) {
|
||||
throw "Undefined value";
|
||||
}
|
||||
this._currentValue = value;
|
||||
}
|
||||
|
||||
public decrementValue() {
|
||||
if (this._currentValue > 0) {
|
||||
this.setCurrentValue(this._currentValue - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public incrementValue() {
|
||||
this.setCurrentValue(this._currentValue + 1);
|
||||
}
|
||||
|
||||
public getCurrentValue(): number {
|
||||
return this._currentValue;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/// <reference path="../typings/index.d.ts" />
|
||||
|
||||
import { Model } from "./model";
|
||||
|
||||
/**
|
||||
* Class view returns the view of a the control rendered to allow
|
||||
* the user to change the value.
|
||||
*/
|
||||
|
||||
export class View {
|
||||
|
||||
private currentValue: string = "";
|
||||
|
||||
constructor(private model: Model, private onInputChanged: Function, private onUpTick: Function, private onDownTick: Function) {
|
||||
this._init();
|
||||
}
|
||||
|
||||
private _init(): void {
|
||||
|
||||
var container = $("<div />");
|
||||
container.addClass("container combo input-text-box emptyBorder");
|
||||
|
||||
var hitcount = $("<input />").attr("type", "string");
|
||||
hitcount.addClass("wrap");
|
||||
container.append(hitcount);
|
||||
|
||||
this.currentValue = String(this.model.getCurrentValue());
|
||||
|
||||
hitcount.val(this.currentValue);
|
||||
hitcount.attr("aria-valuenow", this.currentValue);
|
||||
hitcount.change( () => {
|
||||
this._inputChanged();
|
||||
}).bind('keydown', (evt: JQueryKeyEventObject) => {
|
||||
if (evt.keyCode == 38) {
|
||||
if (this.onUpTick) {
|
||||
this.onUpTick();
|
||||
evt.preventDefault();
|
||||
}
|
||||
}
|
||||
else if (evt.keyCode == 40) {
|
||||
if (this.onDownTick) {
|
||||
this.onDownTick();
|
||||
evt.preventDefault();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var uptick = $("<div />");
|
||||
uptick.click( () => {
|
||||
this.onUpTick();
|
||||
});
|
||||
|
||||
uptick.addClass("bowtie-icon bowtie-arrow-up");
|
||||
|
||||
|
||||
var downtick = $("<div />");
|
||||
downtick.click( () => {
|
||||
this.onDownTick();
|
||||
});
|
||||
|
||||
downtick.addClass("bowtie-icon bowtie-arrow-down");
|
||||
container.append(downtick);
|
||||
container.append(uptick);
|
||||
|
||||
$("body").append(container);
|
||||
}
|
||||
|
||||
private _inputChanged(): void {
|
||||
let newValue = $(".wrap").val();
|
||||
if (this.onInputChanged) {
|
||||
this.onInputChanged(newValue);
|
||||
}
|
||||
}
|
||||
|
||||
public update(value: number) {
|
||||
this.currentValue = String(value);
|
||||
$(".wrap").val("");
|
||||
$(".wrap").val(this.currentValue);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
body {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.bowtie-icon {
|
||||
margin: 0 2px 2px 0;
|
||||
padding: 5px;
|
||||
float: right;
|
||||
line-height: 1.5;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
input {
|
||||
line-height: 1.8;
|
||||
font-size: 16px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.wrap {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.container:hover {
|
||||
border: 1px solid lightgray;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
var allTestFiles = [];
|
||||
var TEST_REGEXP = /(spec|tests)\.js$/i;
|
||||
|
||||
// Get a list of all the test files to include
|
||||
Object.keys(window.__karma__.files).forEach(function (file) {
|
||||
if (TEST_REGEXP.test(file)) {
|
||||
// Normalize paths to RequireJS module names.
|
||||
// If you require sub-dependencies of test files to be loaded as-is (requiring file extension)
|
||||
// then do not normalize the paths
|
||||
var normalizedTestModule = file.replace(/^\/base\/|\.js$/g, '');
|
||||
allTestFiles.push(normalizedTestModule);
|
||||
}
|
||||
})
|
||||
|
||||
require.config({
|
||||
// Karma serves files under /base, which is the basePath from your config file
|
||||
baseUrl: '/base',
|
||||
|
||||
paths: {
|
||||
"chai": "node_modules/chai/chai"
|
||||
},
|
||||
|
||||
// dynamically load all test files
|
||||
deps: allTestFiles,
|
||||
|
||||
callback: window.__karma__.start
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
define(["require", "exports"], function (require, exports) {
|
||||
"use strict";
|
||||
var ErrorView = (function () {
|
||||
function ErrorView(error) {
|
||||
var container = $("<div />");
|
||||
container.addClass("container");
|
||||
var warning = $("<p />");
|
||||
warning.text(error);
|
||||
warning.attr("title", error);
|
||||
container.append(warning);
|
||||
var help = $("<p />");
|
||||
help.text("See ");
|
||||
var a = $("<a> </a>");
|
||||
a.attr("href", "https://www.visualstudio.com/en-us/products/visual-studio-team-services-vs.aspx");
|
||||
a.attr("target", "_blank");
|
||||
a.text("Documentation.");
|
||||
help.append(a);
|
||||
container.append(help);
|
||||
$('body').empty().append(container);
|
||||
}
|
||||
return ErrorView;
|
||||
}());
|
||||
exports.ErrorView = ErrorView;
|
||||
});
|
|
@ -0,0 +1,30 @@
|
|||
define(["require", "exports"], function (require, exports) {
|
||||
"use strict";
|
||||
var Model = (function () {
|
||||
function Model(initialValue) {
|
||||
this._currentValue = initialValue;
|
||||
}
|
||||
Model.prototype.setCurrentValue = function (value) {
|
||||
if (value === undefined) {
|
||||
throw "Undefined value";
|
||||
}
|
||||
this._currentValue = value;
|
||||
};
|
||||
Model.prototype.selectPreviousOption = function () {
|
||||
if (this._currentValue > 0 && this._currentValue !== -1) {
|
||||
this.setCurrentValue(this._currentValue - 1);
|
||||
}
|
||||
else {
|
||||
this.setCurrentValue(0);
|
||||
}
|
||||
};
|
||||
Model.prototype.selectNextOption = function () {
|
||||
this.setCurrentValue(this._currentValue + 1);
|
||||
};
|
||||
Model.prototype.getCurrentValue = function () {
|
||||
return this._currentValue;
|
||||
};
|
||||
return Model;
|
||||
}());
|
||||
exports.Model = Model;
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
define(["require", "exports", 'chai', './model'], function (require, exports, chai_1, model_1) {
|
||||
"use strict";
|
||||
describe("Model", function () {
|
||||
var model;
|
||||
beforeEach(function () {
|
||||
model = new model_1.Model(0);
|
||||
});
|
||||
it("current value of 0", function () {
|
||||
chai_1.expect(model.getCurrentValue()).to.be.deep.equal(0);
|
||||
});
|
||||
it("next value from 0", function () {
|
||||
model.selectNextOption();
|
||||
chai_1.expect(model.getCurrentValue()).to.be.deep.equal(1);
|
||||
});
|
||||
it("previous value of 0", function () {
|
||||
model.selectPreviousOption();
|
||||
chai_1.expect(model.getCurrentValue()).to.be.deep.equal(0);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "amd",
|
||||
"sourceMap": false
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"globalDependencies": {
|
||||
"chai": "registry:dt/chai#3.4.0+20160601211834",
|
||||
"jquery": "registry:dt/jquery#1.10.0+20160628074423",
|
||||
"knockout": "registry:dt/knockout#0.0.0+20160512130947",
|
||||
"mocha": "registry:dt/mocha#2.2.5+20160619032855",
|
||||
"q": "registry:dt/q#0.0.0+20160613154756",
|
||||
"tfs": "npm:vss-web-extension-sdk/typings/tfs.d.ts",
|
||||
"vss": "npm:vss-web-extension-sdk/typings/vss.d.ts"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
"manifestVersion": 1,
|
||||
"id": "hitcount-control",
|
||||
"version": "0.1.0",
|
||||
"name": "HitCount Control",
|
||||
"scopes": [
|
||||
"vso.work",
|
||||
"vso.work_write"
|
||||
],
|
||||
"description": "Describe your extension.",
|
||||
"publisher": "<your-publisher",
|
||||
"icons": {
|
||||
},
|
||||
"targets": [
|
||||
{
|
||||
"id": "Microsoft.VisualStudio.Services"
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Sample"
|
||||
],
|
||||
"content": {
|
||||
"details": {
|
||||
"path": "details.md"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"home": {
|
||||
"uri": "https://bit.ly"
|
||||
},
|
||||
"getstarted": {
|
||||
"uri": "https://bit.ly"
|
||||
},
|
||||
"learn": {
|
||||
"uri": "https://bit.ly"
|
||||
},
|
||||
"support": {
|
||||
"uri": "https://bit.ly"
|
||||
},
|
||||
"repository": {
|
||||
"uri": "https://bit.ly"
|
||||
},
|
||||
"issues": {
|
||||
"uri": "https://bit.ly"
|
||||
}
|
||||
},
|
||||
"branding": {
|
||||
"color": "rgb(220, 235, 252)",
|
||||
"theme": "light"
|
||||
},
|
||||
"files": [
|
||||
{
|
||||
"path": "img",
|
||||
"addressable": true
|
||||
},
|
||||
{
|
||||
"path": "scripts",
|
||||
"addressable": true
|
||||
},
|
||||
{
|
||||
"path": "styles",
|
||||
"addressable": true
|
||||
},
|
||||
{
|
||||
"path": "index.html",
|
||||
"addressable": true
|
||||
}
|
||||
],
|
||||
"categories": [
|
||||
"Integrate"
|
||||
],
|
||||
"contributions": [
|
||||
{
|
||||
"id": "hitcount-control-contribution",
|
||||
"type": "ms.vss-work-web.work-item-form-control",
|
||||
"targets": [
|
||||
"ms.vss-work-web.work-item-form"
|
||||
],
|
||||
"properties": {
|
||||
"name": "Priority",
|
||||
"group": "contributed",
|
||||
"uri": "index.html",
|
||||
"height": 28,
|
||||
"inputs": [
|
||||
{
|
||||
"id": "FieldName",
|
||||
"description": "The field associated with the control.",
|
||||
"validation": {
|
||||
"dataType": "Field",
|
||||
"isRequired": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
Загрузка…
Ссылка в новой задаче